I'm trying to create a Grails application that uses Activiti for its process engine. To that end, I'd like the main Activiti service classes (RuntimeService, TaskService, etc.) to be wired as Spring beans.
I believe that I have the wiring setup correctly, but when I run a simple integration test that calls the runtime service, I get an error that Spring couldn't open a hibernate session (see Full Stack Trace, below).
Update: I can start the application with run-app
, then call a controller action which calls my service, and it all works. So, the Activiti wiring works, it just has some conflict with the Grails Integration Testing mixin.
I really want the Activiti services to use the same datasource connection as the Grails application. I'm assuming that the problem is that Activiti is trying to create its own ConnectionHolder instance, when Grails already has one setup for the integration tests.
My specific (and possibly misguided) question I have is, how do I configure my Activiti ProcessEngine so that it uses the same data source and hibernate connection that my Grails application does?
The more general question is, how can I best make the Activiti services available to my Grails application? I've looked at the Activiti plugin for grails, and the source of it has helped me get this far. However, I'd rather not use that plugin; it's not using the latest activiti, development on it is not terribly active, and it's not really what I need in any case.
Full Stack Trace
| Failure: start approver request(com.package.MyServiceSpec)
| org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@7f2e1821] for key [org.springframework.jdbc.datasource.LazyConnectionDa
at grails.test.mixin.integration.IntegrationTestMixin.initIntegrationTest(IntegrationTestMixin.groovy:58)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
at org.spockframework.runtime.extension.builtin.JUnitFixtureMethodsExtension$FixtureType$FixtureMethodInterceptor.intercept(JUnitFixtureMethodsExtension.java:145)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:84)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
at org.spockframework.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:138)
Caused by: org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@7f2e1821] for key [org.springframework.jdbc.datasource.LazyConn
... 7 more
Caused by: java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder@7f2e1821] for key [org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy@1b7aeb] bound to thread [main]
... 7 more
| Completed 1 integration test, 1 failed in 0m 1s
resources.groovy
import org.activiti.engine.ProcessEngine
import org.activiti.spring.ProcessEngineFactoryBean
import org.activiti.explorer.form.*
import org.activiti.spring.SpringProcessEngineConfiguration
import grails.util.Environment
//These imports are only needed in the test environment for building an h2 database for activiti during unit tests
import org.springframework.jdbc.datasource.DataSourceTransactionManager
import org.springframework.jdbc.datasource.SimpleDriverDataSource
beans = {
processEngineConfig(SpringProcessEngineConfiguration) {
dataSource = ref('dataSource')
transactionManager = ref('transactionManager')
databaseType = 'oracle'
databaseSchema = 'OURSCHEMA'
databaseSchemaUpdate = false
jobExecutorActivate = true
}
processEngine(ProcessEngineFactoryBean) {
processEngineConfiguration = ref("processEngineConfig")
}
runtimeService(processEngine: "getRuntimeService")
repositoryService(processEngine: "getRepositoryService")
taskService(processEngine: "getTaskService")
managementService(processEngine: "getManagementService")
historyService(processEngine: "getHistoryService")
formService(processEngine: "getFormService")
}
Service class
class MyService {
def foapAuthFoapService
def processEngine
def runtimeService
def repositoryService
def taskService
def managementService
def historyService
def formService
/**
* Start the activiti process.
*
*/
def startRequest(String requester, String subject, String designatedApprover) {
runtimeService.startProcessInstanceByKey('MyProcess', ["requester": requester, "subject": subject, "designatedApprover": designatedApprover])
}
}
Spock Test
def "start request"() {
setup:
def approverRequest = service.startRequest(requester, subject, designatedApprover)
def variables = runtimeService.getVariables(approverRequest.id) //approverRequest.getProcessVariables()
expect:
approverRequest instanceof ProcessInstance
variables.entrySet().contailsAll(["designatedApprover": designatedApprover, "requester": requester, "subject": subject].entrySet())
where:
requester | subject | designatedApprover
"abc123" | "def456"| "hij789"
}