Chapter 67. Persistence and transactions in the process engine The process engine implements persistence for process states. The implementation uses the JPA framework with an SQL database backend. It can also store audit log information in the database. The process engine also enables transactional execution of processes using the JTA framework, relying on the persistence backend to support the transactions. 67.1. Persistence of process runtime states The process engine supports persistent storage of the runtime state of running process instances. Because it stores the runtime states, it can continue execution of a process instance if the process engine stopped or encountered a problem at any point. The process engine also persistently stores the process definitions and the history logs of current and process states. You can use the persistence.xml file, specified by the JPA framework, to configure persistence in an SQL database. You can plug in different persistence strategies. For more information about the persistence.xml file, see Section 67.4.1, "Configuration in the persistence.xml file" . By default, if you do not configure persistence in the process engine, process information, including process instance states, is not made persistent. When the process engine starts a process, it creates a process instance , which represents the execution of the process in that specific context. For example, when executing a process that processes a sales order, one process instance is created for each sales request. The process instance contains the current runtime state and context of a process, including current values of any process variables. However, it does not include information about the history of past states of the process, as this information is not required for ongoing execution of a process. When the runtime state of process instances is made persistent, you can restore the state of execution of all running processes in case the process engine fails or is stopped. You can also remove a particular process instance from memory and then restore it at a later time. If you configure the process engine to use persistence, it automatically stores the runtime state into the database. You do not need to trigger persistence in the code. When you restore the state of the process engine from a database, all instances are automatically restored to their last recorded state. Process instances automatically resume execution if they are triggered, for example, by an expired timer, the completion of a task that was requested by the process instance, or a signal being sent to the process instance. You do not need to load separate instances and trigger their execution manually. The process engine also automatically reloads process instances on demand. 67.1.1. Safe points for persistence The process engine saves the state of a process instance to persistent storage at safe points during the execution of the process. When a process instance is started or resumes execution from a wait state, the process engine continues the execution until no more actions can be performed. If no more actions can be performed, it means that the process has completed or else has reached a wait state. If the process contains several parallel paths, all the paths must reach a wait state. This point in the execution of the process is considered a safe point. At this point, the process engine stores the state of the process instance, and of any other process instances that were affected by the execution, to persistent storage. 67.2. The persistent audit log The process engine can store information about the execution of process instances, including the successive historical states of the instances. This information can be useful in many cases. For example, you might want to verify which actions have been executed for a particular process instance or to monitor and analyze the efficiency of a particular process. However, storing history information in the runtime database would result in the database rapidly increasing in size and would also affect the performance of the persistence layer. Therefore, history log information is stored separately. The process engine creates a log based on events that it generates during execution of processes. It uses the event listener mechanism to receive events and extract the necessary information, then persists this information to a database. The jbpm-audit module contains an event listener that stores process-related information in a database using JPA. You can use filters to limit the scope of the logged information. 67.2.1. The process engine audit log data model You can query process engine audit log information to use it in different scenarios, for example, creating a history log for one specific process instance or analyzing the performance of all instances of a specific process. The audit log data model is a default implementation. Depending on your use cases, you might also define your own data model for storing the information you require. You can use process event listeners to extract the information. The data model contains three entities: one for process instance information, one for node instance information, and one for process variable instance information. The ProcessInstanceLog table contains the basic log information about a process instance. Table 67.1. ProcessInstanceLog table fields Field Description Nullable id The primary key and ID of the log entity NOT NULL correlationKey The correlation of this process instance duration Actual duration of this process instance since its start date end_date When applicable, the end date of the process instance externalId Optional external identifier used to correlate to some elements, for example, a deployment ID user_identity Optional identifier of the user who started the process instance outcome The outcome of the process instance. This field contains the error code if the process instance was finished with an error event. parentProcessInstanceId The process instance ID of the parent process instance, if applicable processid The ID of the process processinstanceid The process instance ID NOT NULL processname The name of the process processtype The type of the instance (process or case) processversion The version of the process sla_due_date The due date of the process according to the service level agreement (SLA) slaCompliance The level of compliance with the SLA start_date The start date of the process instance status The status of the process instance that maps to the process instance state The NodeInstanceLog table contains more information about which nodes were executed inside each process instance. Whenever a node instance is entered from one of its incoming connections or is exited through one of its outgoing connections, information about the event is stored in this table. Table 67.2. NodeInstanceLog table fields Field Description Nullable id The primary key and ID of the log entity NOT NULL connection Actual identifier of the sequence flow that led to this node instance log_date The date of the event externalId Optional external identifier used to correlate to some elements, for example, a deployment ID nodeid The node ID of the corresponding node in the process definition nodeinstanceid The node instance ID nodename The name of the node nodetype The type of the node processid The ID of the process that the process instance is executing processinstanceid The process instance ID NOT NULL sla_due_date The due date of the node according to the service level agreement (SLA) slaCompliance The level of compliance with the SLA type The type of the event (0 = enter, 1 = exit) NOT NULL workItemId (Optional, only for certain node types) The identifier of the work item nodeContainerId The identifier of the container, if the node is inside an embedded sub-process node referenceId The reference identifier observation The original node instance ID and job ID, if the node is of the scheduled event type. You can use this information to trigger the job again. The VariableInstanceLog table contains information about changes in variable instances. By default, the process engine generates log entries after a variable changes its value. The process engine can also log entries before the changes. Table 67.3. VariableInstanceLog table fields Field Description Nullable id The primary key and ID of the log entity NOT NULL externalId Optional external identifier used to correlate to some elements, for example, a deployment ID log_date The date of the event processid The ID of the process that the process instance is executing processinstanceid The process instance ID NOT NULL oldvalue The value of the variable at the time that the log is made value The value of the variable at the time that the log is made variableid The variable ID in the process definition variableinstanceid The ID of the variable instance The AuditTaskImpl table contains information about user tasks. Table 67.4. AuditTaskImpl table fields Field Description Nullable id The primary key and ID of the task log entity activationTime Time when this task was activated actualOwner Actual owner assigned to this task. This value is set only when the owner claims the task. createdBy User who created this task createdOn Date when the task was created deploymentId The ID of the deployment of which this task is a part description Description of the task dueDate Due date set on this task name Name of the task parentId Parent task ID priority Priority of the task processId Process definition ID to which this task belongs processInstanceId Process instance ID with which this task is associated processSessionId KIE session ID used to create this task status Current status of the task taskId Identifier of the task workItemId Identifier of the work item assigned on the process side to this task ID lastModificationDate The date and time when the process instance state was last recorded in the persistence database The BAMTaskSummary table collects information about tasks that is used by the BAM engine to build charts and dashboards. Table 67.5. BAMTaskSummary table fields Field Description Nullable pk The primary key and ID of the log entity NOT NULL createdDate Date when the task was created duration Duration since the task was created endDate Date when the task reached an end state (complete, exit, fail, skip) processinstanceid The process instance ID startDate Date when the task was started status Current status of the task taskId Identifier of the task taskName Name of the task userId User ID assigned to the task optlock The version field that serves as its optimistic lock value The TaskVariableImpl table contains information about task variable instances. Table 67.6. TaskVariableImpl table fields Field Description Nullable id The primary key and ID of the log entity NOT NULL modificationDate Date when the variable was modified most recently name Name of the task processid The ID of the process that the process instance is executing processinstanceid The process instance ID taskId Identifier of the task type Type of the variable: either input or output of the task value Variable value The TaskEvent table contains information about changes in task instances. Operations such as claim , start , and stop are stored in this table to provide a timeline view of events that happened to the given task. Table 67.7. TaskEvent table fields Field Description Nullable id The primary key and ID of the log entity NOT NULL logTime Date when this event was saved message Log event message processinstanceid The process instance ID taskId Identifier of the task type Type of the event. Types correspond to life cycle phases of the task userId User ID assigned to the task workItemId Identifier of the work item to which the task is assigned optlock The version field that serves as its optimistic lock value correlationKey Correlation key of the process instance processType Type of the process instance (process or case) currentOwner The current owner of the task 67.2.2. Configuration for storing the process events log in a database To log process history information in a database with a default data model, you must register the logger on your session. Registering the logger on your KIE session KieSession ksession = ...; ksession.addProcessEventListener(AuditLoggerFactory.newInstance(Type.JPA, ksession, null)); // invoke methods for your session here To specify the database for storing the information, you must modify the persistence.xml file to include the audit log classes: ProcessInstanceLog , NodeInstanceLog , and VariableInstanceLog . Modified persistence.xml file that includes the audit log classes <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <persistence version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>jdbc/jbpm-ds</jta-data-source> <mapping-file>META-INF/JBPMorm.xml</mapping-file> <class>org.drools.persistence.info.SessionInfo</class> <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class> <class>org.drools.persistence.info.WorkItemInfo</class> <class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class> <class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class> <class>org.jbpm.runtime.manager.impl.jpa.ContextMappingInfo</class> <class>org.jbpm.process.audit.ProcessInstanceLog</class> <class>org.jbpm.process.audit.NodeInstanceLog</class> <class>org.jbpm.process.audit.VariableInstanceLog</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.connection.release_mode" value="after_transaction"/> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/> </properties> </persistence-unit> </persistence> 67.2.3. Configuration for sending the process events log to a JMS queue When the process engine stores events in the database with the default audit log implementation, the database operation is completed synchronously, within the same transaction as the actual execution of the process instance. This operation takes time, and on highly loaded systems it might have some impact on database performance, especially when both the history log and the runtime data are stored in the same database. As an alternative, you can use the JMS-based logger that the process engine provides. You can configure this logger to submit process log entries as messages to a JMS queue, instead of directly persisting them in the database. You can configure the JMS logger to be transactional, in order to avoid data inconsistencies if a process engine transaction is rolled back. Using the JMS audit logger ConnectionFactory factory = ...; Queue queue = ...; StatefulKnowledgeSession ksession = ...; Map<String, Object> jmsProps = new HashMap<String, Object>(); jmsProps.put("jbpm.audit.jms.transacted", true); jmsProps.put("jbpm.audit.jms.connection.factory", factory); jmsProps.put("jbpm.audit.jms.queue", queue); ksession.addProcessEventListener(AuditLoggerFactory.newInstance(Type.JMS, ksession, jmsProps)); // invoke methods one your session here This is just one of the possible ways to configure JMS audit logger. You can use the AuditLoggerFactory class to set additional configuration parameters. 67.2.4. Auditing of variables By default, values of process and task variables are stored in audit tables as string representations. To create string representations of non-string variable types, the process engine calls the variable.toString() method. If you use a custom class for a variable, you can implement this method for the class. In many cases this representation is sufficient. However, sometimes a string representation in the logs might not be sufficient, especially when there is a need for efficient queries by process or task variables. For example, a Person object, used as a value for a variable, might have the following structure: Example Person object, used as a process or task variable value public class Person implements Serializable { private static final long serialVersionUID = -5172443495317321032L; private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } } The toString() method provides a human-readable format. However, it might not be sufficient for a search. A sample string value is Person [name="john", age="34"] . Searching through a large number of such strings to find people of age 34 would make a database query inefficient. To enable more efficient searching, you can audit variables using VariableIndexer objects, which extract relevant parts of the variable for storage in the audit log. Definition of the VariableIndexer interface /** * Variable indexer that transforms a variable instance into another representation (usually string) * for use in log queries. * * @param <V> type of the object that will represent the indexed variable */ public interface VariableIndexer<V> { /** * Tests if this indexer can index a given variable * * NOTE: only one indexer can be used for a given variable * * @param variable variable to be indexed * @return true if the variable should be indexed with this indexer */ boolean accept(Object variable); /** * Performs an index/transform operation on the variable. The result of this operation can be * either a single value or a list of values, to support complex type separation. * For example, when the variable is of the type Person that has name, address, and phone fields, * the indexer could build three entries out of it to represent individual fields: * person = person.name * address = person.address.street * phone = person.phone * this configuration allows advanced queries for finding relevant entries. * @param name name of the variable * @param variable actual variable value * @return */ List<V> index(String name, Object variable); } The default indexer uses the toString() method to produce a single audit entry for a single variable. Other indexers can return a list of objects from indexing a single variable. To enable efficient queries for the Person type, you can build a custom indexer that indexes a Person instance into separate audit entries, one representing the name and another representing the age. Sample indexer for the Person type public class PersonTaskVariablesIndexer implements TaskVariableIndexer { @Override public boolean accept(Object variable) { if (variable instanceof Person) { return true; } return false; } @Override public List<TaskVariable> index(String name, Object variable) { Person person = (Person) variable; List<TaskVariable> indexed = new ArrayList<TaskVariable>(); TaskVariableImpl personNameVar = new TaskVariableImpl(); personNameVar.setName("person.name"); personNameVar.setValue(person.getName()); indexed.add(personNameVar); TaskVariableImpl personAgeVar = new TaskVariableImpl(); personAgeVar.setName("person.age"); personAgeVar.setValue(person.getAge()+""); indexed.add(personAgeVar); return indexed; } } The process engine can use this indexer to index values when they are of the Person type, while all other variables are indexed with the default toString() method. Now, to query for process instances or tasks that refer to a person with age 34, you can use the following query: variable name: person.age variable value: 34 As a LIKE type query is not used, the database server can optimize the query and make it efficient on a large set of data. Custom indexers The process engine supports indexers for both process and task variables. However, it uses different interfaces for the indexers, because they must produce different types of objects that represent an audit view of the variable. You must implement the following interfaces to build custom indexers: For process variables: org.kie.internal.process.ProcessVariableIndexer For task variables: org.kie.internal.task.api.TaskVariableIndexer You must implement two methods for either of the interfaces: accept : Indicates whether a type is handled by this indexer. The process engine expects that only one indexer can index a given variable value, so it uses the first indexer that accepts the type. index : Indexes a value, producing a object or list of objects (usually strings) for inclusion in the audit log. After implementing the interface, you must package this implementation as a JAR file and list the implementation in one of the following files: For process variables, the META-INF/services/org.kie.internal.process.ProcessVariableIndexer file, which lists fully qualified class names of process variable indexers (single class name per line) For task variables, the META-INF/services/org.kie.internal.task.api.TaskVariableIndexer file, which lists fully qualified class names of task variable indexers (single class name per line) The ServiceLoader mechanism discovers the indexers using these files. When indexing a process or task variable, the process engine examines the registered indexers to find any indexer that accepts the value of the variable. If no other indexer accepts the value, the process engine applies the default indexer that uses the toString() method. 67.3. Transactions in the process engine The process engine supports Java Transaction API (JTA) transactions. The current version of the process engine does not support pure local transactions. If you do not provide transaction boundaries inside your application, the process engine automatically executes each method invocation on the process engine in a separate transaction. Optionally, you can specify the transaction boundaries in the application code, for example, to combine multiple commands into one transaction. 67.3.1. Registration of a transaction manager You must register a transaction manager in the environment to use user-defined transactions. The following sample code registers the transaction manager and uses JTA calls to specify transaction boundaries. Registering a transaction manager and using transactions // Create the entity manager factory EntityManagerFactory emf = EntityManagerFactoryManager.get().getOrCreate("org.jbpm.persistence.jpa"); TransactionManager tm = TransactionManagerServices.getTransactionManager(); // Set up the runtime environment RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .addAsset(ResourceFactory.newClassPathResource("MyProcessDefinition.bpmn2"), ResourceType.BPMN2) .addEnvironmentEntry(EnvironmentName.TRANSACTION_MANAGER, tm) .get(); // Get the KIE session RuntimeManager manager = RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(environment); RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get()); KieSession ksession = runtime.getKieSession(); // Start the transaction UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction"); ut.begin(); // Perform multiple commands inside one transaction ksession.insert( new Person( "John Doe" ) ); ksession.startProcess("MyProcess"); // Commit the transaction ut.commit(); You must provide a jndi.properties file in you root class path to create a JNDI InitialContextFactory object, because transaction-related objects like UserTransaction , TransactionManager , and TransactionSynchronizationRegistry are registered in JNDI. If your project includes the jbpm-test module, this file is already included by default. Otherwise, you must create the jndi.properties file with the following content: Content of the jndi.properties file java.naming.factory.initial=org.jbpm.test.util.CloseSafeMemoryContextFactory org.osjava.sj.root=target/test-classes/config org.osjava.jndi.delimiter=/ org.osjava.sj.jndi.shared=true This configuration assumes that the simple-jndi:simple-jndi artifact is present in the class path of your project. You can also use a different JNDI implementation. By default, the Narayana JTA transaction manager is used. If you want to use a different JTA transaction manager, you can change the persistence.xml file to use the required transaction manager. For example, if your application runs on Red Hat JBoss EAP version 7 or later, you can use the JBoss transaction manager. In this case, change the transaction manager property in the persistence.xml file: Transaction manager property in the persistence.xml file for the JBoss transaction manager <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" /> Warning Using the Singleton strategy of the RuntimeManager class with JTA transactions ( UserTransaction or CMT) creates a race condition. This race condition can result in an IllegalStateException exception with a message similar to Process instance XXX is disconnected . To avoid this race condition, explicitly synchronize around the KieSession instance when invoking the transaction in the user application code. synchronized (ksession) { try { tx.begin(); // use ksession // application logic tx.commit(); } catch (Exception e) { //... } } 67.3.2. Configuring container-managed transactions If you embed the process engine in an application that executes in container-managed transaction (CMT) mode, for example, EJB beans, you must complete additional configuration. This configuration is especially important if the application runs on an application server that does not allow a CMT application to access a UserTransaction instance from JNDI, for example, WebSphere Application Server. The default transaction manager implementation in the process engine relies on UserTransaction to query transaction status and then uses the status to determine whether to start a transaction. In environments that prevent access to a UserTransaction instance, this implementation fails. To enable proper execution in CMT environments, the process engine provides a dedicated transaction manager implementation: org.jbpm.persistence.jta.ContainerManagedTransactionManager . This transaction manager expects that the transaction is active and always returns ACTIVE when the getStatus() method is invoked. Operations such as begin , commit , and rollback are no-op methods, because the transaction manager cannot affect these operations in container-managed transaction mode. Note During process execution your code must propagate any exceptions thrown by the engine to the container to ensure that the container rolls transactions back when necessary. To configure this transaction manager, complete the steps in this procedure. Procedure In your code, insert the transaction manager and persistence context manager into the environment before creating or loading a session: Inserting the transaction manager and persistence context manager into the environment Environment env = EnvironmentFactory.newEnvironment(); env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, emf); env.set(EnvironmentName.TRANSACTION_MANAGER, new ContainerManagedTransactionManager()); env.set(EnvironmentName.PERSISTENCE_CONTEXT_MANAGER, new JpaProcessPersistenceContextManager(env)); env.set(EnvironmentName.TASK_PERSISTENCE_CONTEXT_MANAGER, new JPATaskPersistenceContextManager(env)); In the persistence.xml file, configure the JPA provider. The following example uses hibernate and WebSphere Application Server. Configuring the JPA provider in the persistence.xml file <property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.CMTTransactionFactory"/> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.WebSphereJtaPlatform"/> To dispose a KIE session, do not dispose it directly. Instead, execute the org.jbpm.persistence.jta.ContainerManagedTransactionDisposeCommand command. This commands ensures that the session is disposed at the completion of the current transaction. In the following example, ksession is the KieSession object that you want to dispose. Disposing a KIE session using the ContainerManagedTransactionDisposeCommand command ksession.execute(new ContainerManagedTransactionDisposeCommand()); Directly disposing the session causes an exception at the completion of the transaction, because the process engine registers transaction synchronization to clean up the session state. 67.3.3. Transaction retries When the process engine commits a transaction, sometimes the commit operation fails because another transaction is being committed at the same time. In this case, the process engine must retry the transaction. If several retries fail, the transaction fails permanently. You can use JVM system properties to control the retrying process. Table 67.8. System properties for retrying committing transactions Property Values Default Description org.kie.optlock.retries Integer 5 This property describes how many times the process engine retries a transaction before failing permanently. org.kie.optlock.delay Integer 50 The delay time before the first retry, in milliseconds. org.kie.optlock.delayFactor Integer 4 The multiplier for increasing the delay time for each subsequent retry. With the default values, the process engine waits 50 milliseconds before the first retry, 200 milliseconds before the second retry, 800 milliseconds before the third retry, and so on. 67.4. Configuration of persistence in the process engine If you use the process engine without configuring any persistence, it does not save runtime data to any database; no in-memory database is available by default. You can use this mode if it is required for performance reasons or when you want to manage persistence yourself. To use JPA persistence in the process engine, you must configure it. Configuration usually requires adding the necessary dependencies, configuring a data source, and creating the process engine classes with persistence configured. 67.4.1. Configuration in the persistence.xml file To use JPA persistence, you must add a persistence.xml persistence configuration to your class path to configure JPA to use Hibernate and the H2 database (or any other database that you prefer). Place this file in the META-INF directory of your project. Sample persistence.xml file <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <persistence version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>jdbc/jbpm-ds</jta-data-source> <mapping-file>META-INF/JBPMorm.xml</mapping-file> <class>org.drools.persistence.info.SessionInfo</class> <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class> <class>org.drools.persistence.info.WorkItemInfo</class> <class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class> <class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class> <class>org.jbpm.runtime.manager.impl.jpa.ContextMappingInfo</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.connection.release_mode" value="after_transaction"/> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/> </properties> </persistence-unit> </persistence> The example refers to a jdbc/jbpm-ds data source. For instructions about configuring a data source, see Section 67.4.2, "Configuration of data sources for process engine persistence" . 67.4.2. Configuration of data sources for process engine persistence To configure JPA persistence in the process engine, you must provide a data source, which represents a database backend. If you run your application in an application server, such as Red Hat JBoss EAP, you can use the application server to set up data sources, for example, by adding a data source configuration file in the deploy directory. For instructions about creating data sources, see the documentation for the application server. If you deploy your application to Red Hat JBoss EAP, you can create a data source by creating a configuration file in the deploy directory: Example data source configuration file for Red Hat JBoss EAP <?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>jdbc/jbpm-ds</jndi-name> <connection-url>jdbc:h2:tcp://localhost/~/test</connection-url> <driver-class>org.h2.jdbcx.JdbcDataSource</driver-class> <user-name>sa</user-name> <password></password> </local-tx-datasource> </datasources> If your application runs in a plain Java environment, you can use Narayana and Tomcat DBCP by using the DataSourceFactory class from the kie-test-util module supplied by Red Hat Process Automation Manager. See the following code fragment. This example uses the H2 in-memory database in combination with Narayana and Tomcat DBCP. Example code configuring an H2 in-memory database data source Properties driverProperties = new Properties(); driverProperties.put("user", "sa"); driverProperties.put("password", "sa"); driverProperties.put("url", "jdbc:h2:mem:jbpm-db;MVCC=true"); driverProperties.put("driverClassName", "org.h2.Driver"); driverProperties.put("className", "org.h2.jdbcx.JdbcDataSource"); PoolingDataSourceWrapper pdsw = DataSourceFactory.setupPoolingDataSource("jdbc/jbpm-ds", driverProperties); 67.4.3. Dependencies for persistence Persistence requires certain JAR artifact dependencies. The jbpm-persistence-jpa.jar file is always required. This file contains the code for saving the runtime state whenever necessary. Depending on the persistence solution and database you are using, you might need additional dependencies. The default configuration combination includes the following components: Hibernate as the JPA persistence provider H2 in-memory database Narayana for JTA-based transaction management Tomcat DBCP for connection pooling capabilities This configuration requires the following additional dependencies: jbpm-persistence-jpa ( org.jbpm ) drools-persistence-jpa ( org.drools ) persistence-api ( javax.persistence ) hibernate-entitymanager ( org.hibernate ) hibernate-annotations ( org.hibernate ) hibernate-commons-annotations ( org.hibernate ) hibernate-core ( org.hibernate ) commons-collections ( commons-collections ) dom4j ( org.dom4j ) jta ( javax.transaction ) narayana-jta ( org.jboss.narayana.jta ) tomcat-dbcp ( org.apache.tomcat ) jboss-transaction-api_1.2_spec ( org.jboss.spec.javax.transaction ) javassist ( javassist ) slf4j-api ( org.slf4j ) slf4j-jdk14 ( org.slf4j ) simple-jndi ( simple-jndi ) h2 ( com.h2database ) jbpm-test ( org.jbpm ) only for testing, do not include this artifact in the production application 67.4.4. Creating a KIE session with persistence If your code creates KIE sessions directly, you can use the JPAKnowledgeService class to create your KIE session. This approach provides full access to the underlying configuration. Procedure Create a KIE session using the JPAKnowledgeService class, based on a KIE base, a KIE session configuration (if necessary), and an environment. The environment must contain a reference to the Entity Manager Factory that you use for persistence. Creating a KIE session with persistence // create the entity manager factory and register it in the environment EntityManagerFactory emf = Persistence.createEntityManagerFactory( "org.jbpm.persistence.jpa" ); Environment env = KnowledgeBaseFactory.newEnvironment(); env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf ); // create a new KIE session that uses JPA to store the runtime state StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env ); int sessionId = ksession.getId(); // invoke methods on your method here ksession.startProcess( "MyProcess" ); ksession.dispose(); To re-create a session from the database based on a specific session ID, use the JPAKnowledgeService.loadStatefulKnowledgeSession() method: Re-creating a KIE session from the persistence database // re-create the session from database using the sessionId ksession = JPAKnowledgeService.loadStatefulKnowledgeSession(sessionId, kbase, null, env ); 67.4.5. Persistence in the runtime manager If your code uses the RuntimeManager class, use the RuntimeEnvironmentBuilder class to configure the environment for persistence. By default, the runtime manager searches for the org.jbpm.persistence.jpa persistence unit. The following example creates a KieSession with an empty context. Creating a KIE session with an empty context using the runtime manager RuntimeEnvironmentBuilder builder = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .knowledgeBase(kbase); RuntimeManager manager = RuntimeManagerFactory.Factory.get() .newSingletonRuntimeManager(builder.get(), "com.sample:example:1.0"); RuntimeEngine engine = manager.getRuntimeEngine(EmptyContext.get()); KieSession ksession = engine.getKieSession(); The example requires a KIE base as the kbase parameter. You can use a kmodule.xml KJAR descriptor on the class path to build the KIE base. Building a KIE base from a kmodule.xml KJAR descriptor KieServices ks = KieServices.Factory.get(); KieContainer kContainer = ks.getKieClasspathContainer(); KieBase kbase = kContainer.getKieBase("kbase"); A kmodule.xml descriptor file can include an attribute for resource packages to scan to find and deploy process engine workflows. Sample kmodule.xml descriptor file <kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule"> <kbase name="kbase" packages="com.sample"/> </kmodule> To control the persistence, you can use the RuntimeEnvironmentBuilder::entityManagerFactory methods. Controlling configuration of persistence in the runtime manager EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.jbpm.persistence.jpa"); RuntimeEnvironment runtimeEnv = RuntimeEnvironmentBuilder.Factory .get() .newDefaultBuilder() .entityManagerFactory(emf) .knowledgeBase(kbase) .get(); StatefulKnowledgeSession ksession = (StatefulKnowledgeSession) RuntimeManagerFactory.Factory.get() .newSingletonRuntimeManager(runtimeEnv) .getRuntimeEngine(EmptyContext.get()) .getKieSession(); After creating the ksession KIE session in this example, you can call methods in ksession , for example, StartProcess() . The process engine persists the runtime state in the configured data source. You can restore a process instance from persistent storage by using the process instance ID. The runtime manager automatically re-creates the required session. Re-creating a KIE session from the persistence database using a process instance ID RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstanceId)); KieSession session = runtime.getKieSession(); 67.5. Persisting process variables in a separate database schema in Red Hat Process Automation Manager When you create process variables to use within the processes that you define, Red Hat Process Automation Manager stores those process variables as binary data in a default database schema. You can persist process variables in a separate database schema for greater flexibility in maintaining and implementing your process data. For example, persisting your process variables in a separate database schema can help you perform the following tasks: Maintain process variables in human-readable format Make the variables available to services outside of Red Hat Process Automation Manager Clear the log of the default database tables in Red Hat Process Automation Manager without losing process variable data Note This procedure applies to process variables only. This procedure does not apply to case variables. Prerequisites You have defined processes in Red Hat Process Automation Manager for which you want to implement variables. If you want to persist variables in a database schema outside of Red Hat Process Automation Manager, you have created a data source and the separate database schema that you want to use. For information about creating data sources, see Configuring Business Central settings and properties . Procedure In the data object file that you use as a process variable, add the following elements to configure variable persistence: Example Person.java object configured for variable persistence @javax.persistence.Entity 1 @javax.persistence.Table(name = "Person") 2 public class Person extends org.drools.persistence.jpa.marshaller.VariableEntity 3 implements java.io.Serializable { 4 static final long serialVersionUID = 1L; @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO, generator = "PERSON_ID_GENERATOR") @javax.persistence.Id 5 @javax.persistence.SequenceGenerator(name = "PERSON_ID_GENERATOR", sequenceName = "PERSON_ID_SEQ") private java.lang.Long id; private java.lang.String name; private java.lang.Integer age; public Person() { } public java.lang.Long getId() { return this.id; } public void setId(java.lang.Long id) { this.id = id; } public java.lang.String getName() { return this.name; } public void setName(java.lang.String name) { this.name = name; } public java.lang.Integer getAge() { return this.age; } public void setAge(java.lang.Integer age) { this.age = age; } public Person(java.lang.Long id, java.lang.String name, java.lang.Integer age) { this.id = id; this.name = name; this.age = age; } } 1 Configures the data object as a persistence entity. 2 Defines the database table name used for the data object. 3 Creates a separate MappedVariable mapping table that maintains the relationship between this data object and the associated process instance. If you do not need this relationship maintained, you do not need to extend the VariableEntity class. Without this extension, the data object is still persisted, but contains no additional data. 4 Configures the data object as a serializable object. 5 Sets a persistence ID for the object. To make the data object persistable using Business Central, navigate to the data object file in your project, click the Persistence icon in the upper-right corner of the window, and configure the persistence behavior: Figure 67.1. Persistence configuration in Business Central In the pom.xml file of your project, add the following dependency for persistence support. This dependency contains the VariableEntity class that you configured in your data object. Project dependency for persistence <dependency> <groupId>org.drools</groupId> <artifactId>drools-persistence-jpa</artifactId> <version>USD{rhpam.version}</version> <scope>provided</scope> </dependency> In the ~/META-INF/kie-deployment-descriptor.xml file of your project, configure the JPA marshalling strategy and a persistence unit to be used with the marshaller. The JPA marshalling strategy and persistence unit are required for objects defined as entities. JPA marshaller and persistence unit configured in the kie-deployment-descriptor.xml file <marshalling-strategy> <resolver>mvel</resolver> <identifier>new org.drools.persistence.jpa.marshaller.JPAPlaceholderResolverStrategy("myPersistenceUnit", classLoader)</identifier> <parameters/> </marshalling-strategy> In the ~/META-INF directory of your project, create a persistence.xml file that specifies in which data source you want to persist the process variable: Example persistence.xml file with data source configuration <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"> <persistence-unit name="myPersistenceUnit" transaction-type="JTA"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> 1 <class>org.space.example.Person</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/> <property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.id.new_generator_mappings" value="false"/> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/> </properties> </persistence-unit> </persistence> 1 Sets the data source in which the process variable is persisted To configure the marshalling strategy, persistence unit, and data source using Business Central, navigate to project Settings Deployments Marshalling Strategies and to project Settings Persistence : Figure 67.2. JPA marshaller configuration in Business Central Figure 67.3. Persistence unit and data source configuration in Business Central

Commands:
KieSession ksession = ...; ksession.addProcessEventListener(AuditLoggerFactory.newInstance(Type.JPA, ksession, null)); // invoke methods for your session here
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <persistence version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>jdbc/jbpm-ds</jta-data-source> <mapping-file>META-INF/JBPMorm.xml</mapping-file> <class>org.drools.persistence.info.SessionInfo</class> <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class> <class>org.drools.persistence.info.WorkItemInfo</class> <class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class> <class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class> <class>org.jbpm.runtime.manager.impl.jpa.ContextMappingInfo</class> <class>org.jbpm.process.audit.ProcessInstanceLog</class> <class>org.jbpm.process.audit.NodeInstanceLog</class> <class>org.jbpm.process.audit.VariableInstanceLog</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.connection.release_mode" value="after_transaction"/> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/> </properties> </persistence-unit> </persistence>
ConnectionFactory factory = ...; Queue queue = ...; StatefulKnowledgeSession ksession = ...; Map<String, Object> jmsProps = new HashMap<String, Object>(); jmsProps.put("jbpm.audit.jms.transacted", true); jmsProps.put("jbpm.audit.jms.connection.factory", factory); jmsProps.put("jbpm.audit.jms.queue", queue); ksession.addProcessEventListener(AuditLoggerFactory.newInstance(Type.JMS, ksession, jmsProps)); // invoke methods one your session here
public class Person implements Serializable { private static final long serialVersionUID = -5172443495317321032L; private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
/** * Variable indexer that transforms a variable instance into another representation (usually string) * for use in log queries. * * @param <V> type of the object that will represent the indexed variable */ public interface VariableIndexer<V> { /** * Tests if this indexer can index a given variable * * NOTE: only one indexer can be used for a given variable * * @param variable variable to be indexed * @return true if the variable should be indexed with this indexer */ boolean accept(Object variable); /** * Performs an index/transform operation on the variable. The result of this operation can be * either a single value or a list of values, to support complex type separation. * For example, when the variable is of the type Person that has name, address, and phone fields, * the indexer could build three entries out of it to represent individual fields: * person = person.name * address = person.address.street * phone = person.phone * this configuration allows advanced queries for finding relevant entries. * @param name name of the variable * @param variable actual variable value * @return */ List<V> index(String name, Object variable); }
public class PersonTaskVariablesIndexer implements TaskVariableIndexer { @Override public boolean accept(Object variable) { if (variable instanceof Person) { return true; } return false; } @Override public List<TaskVariable> index(String name, Object variable) { Person person = (Person) variable; List<TaskVariable> indexed = new ArrayList<TaskVariable>(); TaskVariableImpl personNameVar = new TaskVariableImpl(); personNameVar.setName("person.name"); personNameVar.setValue(person.getName()); indexed.add(personNameVar); TaskVariableImpl personAgeVar = new TaskVariableImpl(); personAgeVar.setName("person.age"); personAgeVar.setValue(person.getAge()+""); indexed.add(personAgeVar); return indexed; } }
// Create the entity manager factory EntityManagerFactory emf = EntityManagerFactoryManager.get().getOrCreate("org.jbpm.persistence.jpa"); TransactionManager tm = TransactionManagerServices.getTransactionManager(); // Set up the runtime environment RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .addAsset(ResourceFactory.newClassPathResource("MyProcessDefinition.bpmn2"), ResourceType.BPMN2) .addEnvironmentEntry(EnvironmentName.TRANSACTION_MANAGER, tm) .get(); // Get the KIE session RuntimeManager manager = RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(environment); RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get()); KieSession ksession = runtime.getKieSession(); // Start the transaction UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction"); ut.begin(); // Perform multiple commands inside one transaction ksession.insert( new Person( "John Doe" ) ); ksession.startProcess("MyProcess"); // Commit the transaction ut.commit();
java.naming.factory.initial=org.jbpm.test.util.CloseSafeMemoryContextFactory org.osjava.sj.root=target/test-classes/config org.osjava.jndi.delimiter=/ org.osjava.sj.jndi.shared=true
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
synchronized (ksession) { try { tx.begin(); // use ksession // application logic tx.commit(); } catch (Exception e) { // } }
Environment env = EnvironmentFactory.newEnvironment(); env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, emf); env.set(EnvironmentName.TRANSACTION_MANAGER, new ContainerManagedTransactionManager()); env.set(EnvironmentName.PERSISTENCE_CONTEXT_MANAGER, new JpaProcessPersistenceContextManager(env)); env.set(EnvironmentName.TASK_PERSISTENCE_CONTEXT_MANAGER, new JPATaskPersistenceContextManager(env));
<property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.CMTTransactionFactory"/> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.WebSphereJtaPlatform"/>
ksession.execute(new ContainerManagedTransactionDisposeCommand());
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <persistence version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>jdbc/jbpm-ds</jta-data-source> <mapping-file>META-INF/JBPMorm.xml</mapping-file> <class>org.drools.persistence.info.SessionInfo</class> <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class> <class>org.drools.persistence.info.WorkItemInfo</class> <class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class> <class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class> <class>org.jbpm.runtime.manager.impl.jpa.ContextMappingInfo</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.connection.release_mode" value="after_transaction"/> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/> </properties> </persistence-unit> </persistence>
<?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>jdbc/jbpm-ds</jndi-name> <connection-url>jdbc:h2:tcp://localhost/~/test</connection-url> <driver-class>org.h2.jdbcx.JdbcDataSource</driver-class> <user-name>sa</user-name> <password></password> </local-tx-datasource> </datasources>
Properties driverProperties = new Properties(); driverProperties.put("user", "sa"); driverProperties.put("password", "sa"); driverProperties.put("url", "jdbc:h2:mem:jbpm-db;MVCC=true"); driverProperties.put("driverClassName", "org.h2.Driver"); driverProperties.put("className", "org.h2.jdbcx.JdbcDataSource"); PoolingDataSourceWrapper pdsw = DataSourceFactory.setupPoolingDataSource("jdbc/jbpm-ds", driverProperties);
// create the entity manager factory and register it in the environment EntityManagerFactory emf = Persistence.createEntityManagerFactory( "org.jbpm.persistence.jpa" ); Environment env = KnowledgeBaseFactory.newEnvironment(); env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf ); // create a new KIE session that uses JPA to store the runtime state StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env ); int sessionId = ksession.getId(); // invoke methods on your method here ksession.startProcess( "MyProcess" ); ksession.dispose();
// re-create the session from database using the sessionId ksession = JPAKnowledgeService.loadStatefulKnowledgeSession(sessionId, kbase, null, env );
RuntimeEnvironmentBuilder builder = RuntimeEnvironmentBuilder.Factory.get() .newDefaultBuilder() .knowledgeBase(kbase); RuntimeManager manager = RuntimeManagerFactory.Factory.get() .newSingletonRuntimeManager(builder.get(), "com.sample:example:1.0"); RuntimeEngine engine = manager.getRuntimeEngine(EmptyContext.get()); KieSession ksession = engine.getKieSession();
KieServices ks = KieServices.Factory.get(); KieContainer kContainer = ks.getKieClasspathContainer(); KieBase kbase = kContainer.getKieBase("kbase");
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule"> <kbase name="kbase" packages="com.sample"/> </kmodule>
EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.jbpm.persistence.jpa"); RuntimeEnvironment runtimeEnv = RuntimeEnvironmentBuilder.Factory .get() .newDefaultBuilder() .entityManagerFactory(emf) .knowledgeBase(kbase) .get(); StatefulKnowledgeSession ksession = (StatefulKnowledgeSession) RuntimeManagerFactory.Factory.get() .newSingletonRuntimeManager(runtimeEnv) .getRuntimeEngine(EmptyContext.get()) .getKieSession();
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get(processInstanceId)); KieSession session = runtime.getKieSession();
@javax.persistence.Entity 1 @javax.persistence.Table(name = "Person") 2 public class Person extends org.drools.persistence.jpa.marshaller.VariableEntity 3 implements java.io.Serializable { 4 static final long serialVersionUID = 1L; @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO, generator = "PERSON_ID_GENERATOR") @javax.persistence.Id 5 @javax.persistence.SequenceGenerator(name = "PERSON_ID_GENERATOR", sequenceName = "PERSON_ID_SEQ") private java.lang.Long id; private java.lang.String name; private java.lang.Integer age; public Person() { } public java.lang.Long getId() { return this.id; } public void setId(java.lang.Long id) { this.id = id; } public java.lang.String getName() { return this.name; } public void setName(java.lang.String name) { this.name = name; } public java.lang.Integer getAge() { return this.age; } public void setAge(java.lang.Integer age) { this.age = age; } public Person(java.lang.Long id, java.lang.String name, java.lang.Integer age) { this.id = id; this.name = name; this.age = age; } }
<dependency> <groupId>org.drools</groupId> <artifactId>drools-persistence-jpa</artifactId> <version>USD{rhpam.version}</version> <scope>provided</scope> </dependency>
<marshalling-strategy> <resolver>mvel</resolver> <identifier>new org.drools.persistence.jpa.marshaller.JPAPlaceholderResolverStrategy("myPersistenceUnit", classLoader)</identifier> <parameters/> </marshalling-strategy>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"> <persistence-unit name="myPersistenceUnit" transaction-type="JTA"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> 1 <class>org.space.example.Person</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/> <property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.id.new_generator_mappings" value="false"/> <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/> </properties> </persistence-unit> </persistence>