cancel
Showing results for 
Search instead for 
Did you mean: 

Create JPA entity in service task

chris_joelly
Champ in-the-making
Champ in-the-making
Hello,

is it possible to create an entity using JPA within an service task and store it as
process variable and use it in other service tasks? I have instantiated a @Entity
in an service task and stored it as process variable. The second service task
then is not able to retrieve that entity, nor is the entity persisted in the database:


Caused by: org.activiti.engine.ActivitiException: Entity does not exist: erp.model.test.TestErp - 801
   at org.activiti.engine.impl.variable.JPAEntityMappings.findEntity(JPAEntityMappings.java:130)
   at org.activiti.engine.impl.variable.JPAEntityMappings.getJPAEntity(JPAEntityMappings.java:119)
   at org.activiti.engine.impl.variable.JPAEntityVariableType.getValue(JPAEntityVariableType.java:77)
   at org.activiti.engine.impl.persistence.entity.VariableInstanceEntity.getValue(VariableInstanceEntity.java:158)
   at org.activiti.engine.impl.persistence.entity.VariableScopeImpl.getVariable(VariableScopeImpl.java:93)
   at erp.domain.activiti.tasks.service.SecondTask.execute(SecondTask.java:29)

and when i create the entity with an EJB which i lookup via JNDI in the service task then the
entity is created in the database and i can store it as process variable. But changes to that
process variable in service tasks are not synchronized to the database. The entity is only
available as process variable.

How can i get it synchronized so that the database and the EJB and the service tasks see the
same entity?

Thanks,

Chris
4 REPLIES 4

ronald_van_kuij
Champ on-the-rise
Champ on-the-rise
if the creation and retrieval are not done in exactly the same entity manager, it is only 'visible' after a commit. Creating it in an ejb remotely could cause it to not be created in the same entity manager. This could also be the case if the creation is done in a timer event etc… So for us to help you, you have to be more specific on what you do.

chris_joelly
Champ in-the-making
Champ in-the-making
Ok, thanks for your time. Here is a short description of my app environment:

Activiti is booted using a singleton EJB which i can inject via @EJB to the
managed JSF beans and the app uses JTA with a MySQL datasource. For Activiti the
datasource and the transaction manager are lookup up via JNDI and configured
using JtaProcessEngineConfiguration.

To be honest, i think i mixed up things on transaction handling a lot. I started
off with a small JSF webapp to display the deployed process definitions, upload
and deploy new process definitions, start new process instances and examine the
task list for the logged in user and the groups he is a member of. The formKey
of the startTask and userTasks are used to specify the corresponding JSF form
which uses @ManagedBean or @Named beans, and the serviceTask uses normal Java
classes for some business logic. The startForms managed bean sets the form
fields to process variables and then starts a process instance.

Without persistence other then using simple Java objects as process variables
everything works fine.

Then i tried to create a JPA entity in the first serviceTask just to see if it
is possible to work with JPA persistence within serviceTasks and stored it as
process variable as well. The JPA entity i created with an statelss EJB which i
looked up via JNDI as well, because i did not find another possibility to get to
an persistemce contest within an serviceTask (JavaDelegate). I thought this might
be a reaonable use case because this way i can place business logic in reusable
session beans. From the next serviceTask in the execution chain i tried to modify
the process variable and investigate its modification in the database, because i
assumed that Activiti might update this JPA process variable, not only in the
store for its process variables but in the entities database table as well. But
unfortunately this is not the case. When i reload this entity using the stateless
session bean then i can only read the initial created entity bean.

Then i tried to create the JPA entity from the JSF managed bean already with data
from the JSF form and set it as process variable when i start off the process
instance (opposite to creating the entity from within the first serviceTask).
But too it is not possible to read the entity between the serviceTasks from the
process variable and the session bean so it reflects the same value. From the
stack trace i saw during my debugging sessions i have the impression that a
process instance is executed the whole execution path all the way through till
the process instance arrives at a wait state? E.g. a userTask? Is this the moment
the process variables are persisted to the database? And is this the moment a
transaction in the Activiti world ends? At least i think so, but i dont know it
yet.

So, i think my problem is that i am not able to integrate transaction handling
between the stateless session bean and Activiti. I thought JTA on Activiti's
and the EJB's side will do the trick, but i am not sure if i am using it the
right way. I dont know too if i need to configure Activiti in a way, besides the
datasource and the transaction manager, so it is aware of the JPA persistence
unit to be able to handle JPA process variables.

activiti.cfg.xml:

<jee:jndi-lookup jndi-name="jdbc/MysqlErp" id="dataSource" />
<jee:jndi-lookup jndi-name="java:appserver/TransactionManager" id="transactionManager" />
<bean id="processEngineConfiguration"
  class="org.activiti.engine.impl.cfg.JtaProcessEngineConfiguration">
  <property name="dataSource" ref="dataSource" />
  <property name="transactionManager" ref="transactionManager"/>
  <property name="transactionsExternallyManaged" value="true" />
  <property name="databaseSchemaUpdate" value="true" />
  <property name="databaseType" value="mysql" />
  <property name="jobExecutorActivate" value="false" />
</bean>

StartBean:

@Named
@RequestScoped
public class SimpleStartBean {

@EJB ActivitiServiceEJB activiti;

@EJB TestErpBean testErp;

private String processDefinitionId;
private String column;

// setters and getters removed…

public String startProcess() throws NamingException, NotSupportedException {

  TestErpBean testErpBean = (TestErpBean) new InitialContext()
    .lookup("java:global/erp.web/TestErpBean");
  TestErp entity = testErpBean.createTestErp();
  entity.setCol(column);
  testErpBean.saveTestErp(entity);

  Map<String, Object> variables = new HashMap<String, Object>();
  variables.put("entity", entity);

  activiti.getProcessEngine().getRuntimeService()
    .startProcessInstanceById(processDefinitionId, variables);
  return "owntasklist";
}
}

TestErp EJB:

@Stateless
@LocalBean
public class TestErpBean {

@PersistenceUnit(unitName="erp.web")
EntityManagerFactory emf;

    public TestErpBean() {
    }
   
    public TestErp createTestErp() {
     EntityManager em = emf.createEntityManager();
  TestErp erp = new TestErp();
  em.persist(erp);
  return erp;
    }
   
    public void saveTestErp(TestErp testErp) {
     EntityManager em = emf.createEntityManager();
     em.persist(em.merge(testErp));
    }
   
    public TestErp findTestErp(final int id) {
     EntityManager em = emf.createEntityManager();
     return em.find(TestErp.class, new Integer(id));
    }
}

firstTask:

public class FirstTask extends BaseTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) throws Exception {

  TestErp entity = (TestErp) execution.getVariable("entity");
  entity.setCol(entity.getCol() + " FIRST");

  logger.log(Level.FINE, "FirstTask.execute: entity.id = {0} entity.col = {1}", new Object[] {new Integer(entity.getId()), entity.getCol()});
}
}

secondTask:

public class SecondTask extends BaseTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) throws Exception {

  TestErp entity = (TestErp) execution.getVariable("entity");
  entity.setCol(entity.getCol()+ " SECOND");
 
  logger.log(Level.FINE, "SecondTask.execute: entity.id = {0} entity.col = {1}", new Object[] {new Integer(entity.getId()), entity.getCol()});
}
}

In the log i see that the field col of the entity is set to "xxx FIRST" and then
"xxx FIRST SECOND", but when i read the process variable after the second service
task from an managed bean and display the value i only get "xxx", which is persisted
in the database too. And "while" Activiti executes the first and second service task
after starting the process instance (SimpleStartBean.startProcess()) the "owntasklist"
should be displayed, which results in the following exception when the
TaskList.getOwnTasks() managed bean method is executed to get the task list from the
ActivitiServiceEJB.


java.lang.NullPointerException
at com.sun.enterprise.resource.ConnectorXAResource.getResourceHandle(ConnectorXAResource.java:246)
at com.sun.enterprise.resource.ConnectorXAResource.start(ConnectorXAResource.java:136)
at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.enlistResource(JavaEETransactionManagerSimplified.java:375)
at com.sun.enterprise.resource.rm.ResourceManagerImpl.registerResource(ResourceManagerImpl.java:152)
at com.sun.enterprise.resource.rm.ResourceManagerImpl.enlistResource(ResourceManagerImpl.java:112)
at com.sun.enterprise.resource.pool.PoolManagerImpl.getResource(PoolManagerImpl.java:208)
at com.sun.enterprise.connectors.ConnectionManagerImpl.getResource(ConnectionManagerImpl.java:338)
at com.sun.enterprise.connectors.ConnectionManagerImpl.internalGetConnection(ConnectionManagerImpl.java:301)
at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:190)
at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:165)
at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:160)
at com.sun.gjc.spi.base.DataSource.getConnection(DataSource.java:113)
at org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource(DefaultSqlSessionFactory.java:72)
at org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession(DefaultSqlSessionFactory.java:32)
at org.activiti.engine.impl.db.DbSqlSession.<init>(DbSqlSession.java:86)
at org.activiti.engine.impl.db.DbSqlSessionFactory.openSession(DbSqlSessionFactory.java:77)
at org.activiti.engine.impl.interceptor.CommandContext.getSession(CommandContext.java:177)
at org.activiti.engine.impl.cfg.standalone.StandaloneMybatisTransactionContext.getDbSqlSession(StandaloneMybatisTransactionContext.java:77)
at org.activiti.engine.impl.cfg.standalone.StandaloneMybatisTransactionContext.rollback(StandaloneMybatisTransactionContext.java:91)
at org.activiti.engine.impl.interceptor.CommandContext.close(CommandContext.java:120)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:49)
at org.activiti.engine.impl.interceptor.JtaTransactionInterceptor.execute(JtaTransactionInterceptor.java:59)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33)
at org.activiti.engine.impl.AbstractQuery.list(AbstractQuery.java:112)
at erp.web.faces.beans.activiti.TaskList.getOwnTasks(TaskList.java:62)

ActivitiServiceEJB:

@Singleton
@LocalBean
@Startup
public class ActivitiServiceEJB {

/** the reference to the application wide Activiti process engine. */
ProcessEngine processEngine = null;

private static Logger logger = Logger.getLogger(ActivitiServiceEJB.class.getName());

    public ActivitiServiceEJB() {
    }
   
    @PostConstruct
    void initEngine() {
     ProcessEngines.init();
     setProcessEngine(ProcessEngines.getDefaultProcessEngine());
     logger.log(Level.INFO, "Process engine {0} created and initialized.", getProcessEngine());
     createActivitiUserData();
    }
   
    @PreRemove
    void destructEngines() {
     ProcessEngines.destroy();
     logger.log(Level.INFO, "Process engines destroyed.");
    }

public ProcessEngine getProcessEngine() {
  return processEngine;
}

public void setProcessEngine(ProcessEngine processEngine) {
  this.processEngine = processEngine;
}

     // removed createActivitiUserData() …
}

TaskList bean:

@ManagedBean
@RequestScoped
public class TaskList {

@EJB
ActivitiServiceEJB activiti;

@ManagedProperty(value = "#{userSession}")
UserSession userSession;

public UserSession getUserSession() {
  return userSession;
}

public void setUserSession(UserSession userSession) {
  this.userSession = userSession;
}

public List<Task> getOwnTasks() {

  List<Task> tasks = null;

  if (userSession.isAdmin()) {
   tasks = activiti.getProcessEngine().getTaskService()
     .createTaskQuery().orderByTaskCreateTime().asc().list();
  } else {
   tasks = activiti.getProcessEngine().getTaskService()
     .createTaskQuery()
     .taskAssignee(userSession.getUser().getUsername())
     .orderByTaskCreateTime().asc().list();
  }

  return tasks;
}

public String getFormKey(String taskId) {
  String formKey = activiti.getProcessEngine().getFormService()
    .getTaskFormData(taskId).getFormKey();
  return formKey;
}

So, this is almost the complete test application and i hope i am not too specific Smiley Happy
I hope it clarifys what code leeds to the mess i have with the transactions. I
tried a lot of different cfg and code snippets, and i am too confused already to
see an possibility to get the thing working right Smiley Happy

Thanks in advance

Chris

ronald_van_kuij
Champ on-the-rise
Champ on-the-rise
From the
stack trace i saw during my debugging sessions i have the impression that a
process instance is executed the whole execution path all the way through till
the process instance arrives at a wait state? E.g. a userTask? Is this the moment
the process variables are persisted to the database? And is this the moment a
transaction in the Activiti world ends? At least i think so, but i dont know it
yet.

Will go into other things later, but this assumption is correct.

I you want persistence in between, vote for http://jira.codehaus.org/browse/ACT-126

chris_joelly
Champ in-the-making
Champ in-the-making
Hello Ronald,

did u have the time to look into that issue?

Thanks in advance

Chris