/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* http://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.
*/
package org.jbpm.process.audit.jms;
import static org.jbpm.persistence.util.PersistenceUtil.JBPM_PERSISTENCE_UNIT_NAME;
import static org.jbpm.persistence.util.PersistenceUtil.cleanUp;
import static org.jbpm.persistence.util.PersistenceUtil.createEnvironment;
import static org.jbpm.persistence.util.PersistenceUtil.setupWithPoolingDataSource;
import static org.jbpm.process.audit.AbstractAuditLogServiceTest.createKieSession;
import static org.jbpm.process.audit.AbstractAuditLogServiceTest.createKnowledgeBase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.XAConnectionFactory;
import javax.naming.InitialContext;
import javax.persistence.EntityManagerFactory;
import javax.transaction.UserTransaction;
import org.hornetq.jms.server.embedded.EmbeddedJMS;
import org.jbpm.process.audit.AbstractAuditLogger;
import org.jbpm.process.audit.AuditLogService;
import org.jbpm.process.audit.AuditLoggerFactory;
import org.jbpm.process.audit.AuditLoggerFactory.Type;
import org.jbpm.process.audit.JPAAuditLogService;
import org.jbpm.process.audit.NodeInstanceLog;
import org.jbpm.process.audit.ProcessInstanceLog;
import org.jbpm.process.audit.VariableInstanceLog;
import org.jbpm.process.instance.impl.demo.SystemOutWorkItemHandler;
import org.jbpm.test.util.AbstractBaseTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.kie.api.runtime.Environment;
import org.kie.api.runtime.EnvironmentName;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.KnowledgeBase;
import org.kie.internal.runtime.StatefulKnowledgeSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import bitronix.tm.resource.jms.PoolingConnectionFactory;
public class AsyncAuditLogProducerTest extends AbstractBaseTest {
private static final Logger logger = LoggerFactory.getLogger(AsyncAuditLogProducerTest.class);
private HashMap<String, Object> context;
private ConnectionFactory factory;
private Queue queue;
private EmbeddedJMS jmsServer;
@Before
public void setup() throws Exception {
startHornetQServer();
context = setupWithPoolingDataSource(JBPM_PERSISTENCE_UNIT_NAME);
}
@After
public void tearDown() throws Exception {
cleanUp(context);
stopHornetQServer();
}
@Test
public void testAsyncAuditProducer() throws Exception {
Environment env = createEnvironment(context);
// load the process
KnowledgeBase kbase = createKnowledgeBase();
// create a new session
StatefulKnowledgeSession session = createSession(kbase, env);
Map<String, Object> jmsProps = new HashMap<String, Object>();
jmsProps.put("jbpm.audit.jms.transacted", false);
jmsProps.put("jbpm.audit.jms.connection.factory", factory);
jmsProps.put("jbpm.audit.jms.queue", queue);
AbstractAuditLogger logger = AuditLoggerFactory.newInstance(Type.JMS, session, jmsProps);
assertNotNull(logger);
assertTrue((logger instanceof AsyncAuditLogProducer));
// start process instance
long processInstanceId = session.startProcess("com.sample.ruleflow").getId();
// setup listener
MessageReceiver receiver = new MessageReceiver();
List<Message> messages = receiver.receive(queue);
assertNotNull(messages);
assertEquals(11, messages.size());
}
@Test
public void testAsyncAuditProducerTransactional() throws Exception {
UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction");
ut.begin();
Environment env = createEnvironment(context);
// load the process
KnowledgeBase kbase = createKnowledgeBase();
// create a new session
StatefulKnowledgeSession session = createSession(kbase, env);
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);
AbstractAuditLogger logger = AuditLoggerFactory.newInstance(Type.JMS, session, jmsProps);
assertNotNull(logger);
assertTrue((logger instanceof AsyncAuditLogProducer));
// start process instance
long processInstanceId = session.startProcess("com.sample.ruleflow").getId();
ut.commit();
MessageReceiver receiver = new MessageReceiver();
List<Message> messages = receiver.receive(queue);
assertNotNull(messages);
assertEquals(11, messages.size());
}
@Test
public void testAsyncAuditProducerTransactionalWithRollback() throws Exception {
UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction");
ut.begin();
Environment env = createEnvironment(context);
// load the process
KnowledgeBase kbase = createKnowledgeBase();
// create a new session
StatefulKnowledgeSession session = createSession(kbase, env);
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);
AbstractAuditLogger logger = AuditLoggerFactory.newInstance(Type.JMS, session, jmsProps);
assertNotNull(logger);
assertTrue((logger instanceof AsyncAuditLogProducer));
// start process instance
long processInstanceId = session.startProcess("com.sample.ruleflow").getId();
ut.rollback();
MessageReceiver receiver = new MessageReceiver();
List<Message> messages = receiver.receive(queue);
assertNotNull(messages);
assertEquals(0, messages.size());
}
@Test
public void testAsyncAuditProducerNonTransactionalWithRollback() throws Exception {
UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction");
ut.begin();
Environment env = createEnvironment(context);
// load the process
KnowledgeBase kbase = createKnowledgeBase();
// create a new session
StatefulKnowledgeSession session = createSession(kbase, env);
Map<String, Object> jmsProps = new HashMap<String, Object>();
jmsProps.put("jbpm.audit.jms.transacted", false);
// do not use bitronix managed connection factory as it enlists it regardless of if queue session
// is transacted or not thus always participates in transaction
jmsProps.put("jbpm.audit.jms.connection.factory", jmsServer.lookup("ConnectionFactory"));
jmsProps.put("jbpm.audit.jms.queue", queue);
AbstractAuditLogger logger = AuditLoggerFactory.newInstance(Type.JMS, session, jmsProps);
assertNotNull(logger);
assertTrue((logger instanceof AsyncAuditLogProducer));
// start process instance
long processInstanceId = session.startProcess("com.sample.ruleflow").getId();
ut.rollback();
MessageReceiver receiver = new MessageReceiver();
List<Message> messages = receiver.receive(queue);
assertNotNull(messages);
assertEquals(11, messages.size());
}
@Test
public void testAsyncAuditLoggerComplete() throws Exception {
Environment env = createEnvironment(context);
// load the process
KnowledgeBase kbase = createKnowledgeBase();
// create a new session
StatefulKnowledgeSession session = createSession(kbase, env);
Map<String, Object> jmsProps = new HashMap<String, Object>();
jmsProps.put("jbpm.audit.jms.transacted", false);
jmsProps.put("jbpm.audit.jms.connection.factory", factory);
jmsProps.put("jbpm.audit.jms.queue", queue);
AbstractAuditLogger logger = AuditLoggerFactory.newInstance(Type.JMS, session, jmsProps);
assertNotNull(logger);
assertTrue((logger instanceof AsyncAuditLogProducer));
// start process instance
ProcessInstance processInstance = session.startProcess("com.sample.ruleflow");
MessageReceiver receiver = new MessageReceiver();
receiver.receiveAndProcess(queue, ((EntityManagerFactory)env.get(EnvironmentName.ENTITY_MANAGER_FACTORY)));
// validate if everything is stored in db
AuditLogService logService = new JPAAuditLogService(env);
List<ProcessInstanceLog> processInstances = logService.findProcessInstances("com.sample.ruleflow");
assertEquals(1, processInstances.size());
List<NodeInstanceLog> nodeInstances = logService.findNodeInstances(processInstance.getId());
assertEquals(6, nodeInstances.size());
for (NodeInstanceLog nodeInstance: nodeInstances) {
assertEquals(processInstance.getId(), nodeInstance.getProcessInstanceId().longValue());
assertEquals("com.sample.ruleflow", nodeInstance.getProcessId());
assertNotNull(nodeInstance.getDate());
}
logService.clear();
processInstances = logService.findProcessInstances("com.sample.ruleflow");
logService.dispose();
assertTrue(processInstances.isEmpty());
}
@Test
public void testAsyncAuditLoggerCompleteDirectCreation() throws Exception {
Environment env = createEnvironment(context);
// load the process
KnowledgeBase kbase = createKnowledgeBase();
// create a new session
StatefulKnowledgeSession session = createSession(kbase, env);
AbstractAuditLogger logger = AuditLoggerFactory.newJMSInstance(true, factory, queue);
assertNotNull(logger);
assertTrue((logger instanceof AsyncAuditLogProducer));
session.addEventListener(logger);
// start process instance
ProcessInstance processInstance = session.startProcess("com.sample.ruleflow");
MessageReceiver receiver = new MessageReceiver();
receiver.receiveAndProcess(queue, ((EntityManagerFactory)env.get(EnvironmentName.ENTITY_MANAGER_FACTORY)));
// validate if everything is stored in db
AuditLogService logService = new JPAAuditLogService(env);
List<ProcessInstanceLog> processInstances = logService.findProcessInstances("com.sample.ruleflow");
assertEquals(1, processInstances.size());
List<NodeInstanceLog> nodeInstances = logService.findNodeInstances(processInstance.getId());
assertEquals(6, nodeInstances.size());
for (NodeInstanceLog nodeInstance: nodeInstances) {
assertEquals(processInstance.getId(), nodeInstance.getProcessInstanceId().longValue());
assertEquals("com.sample.ruleflow", nodeInstance.getProcessId());
assertNotNull(nodeInstance.getDate());
}
logService.clear();
processInstances = logService.findProcessInstances("com.sample.ruleflow");
logService.dispose();
assertTrue(processInstances.isEmpty());
}
@Test
public void testAsyncAuditLoggerCompleteWithVariables() throws Exception {
Environment env = createEnvironment(context);
// load the process
KnowledgeBase kbase = createKnowledgeBase();
// create a new session
StatefulKnowledgeSession session = createSession(kbase, env);
Map<String, Object> jmsProps = new HashMap<String, Object>();
jmsProps.put("jbpm.audit.jms.transacted", false);
jmsProps.put("jbpm.audit.jms.connection.factory", factory);
jmsProps.put("jbpm.audit.jms.queue", queue);
AbstractAuditLogger logger = AuditLoggerFactory.newInstance(Type.JMS, session, jmsProps);
assertNotNull(logger);
assertTrue((logger instanceof AsyncAuditLogProducer));
Map<String, Object> params = new HashMap<String, Object>();
params.put("s", "test value");
// start process instance
ProcessInstance processInstance = session.startProcess("com.sample.ruleflow3", params);
MessageReceiver receiver = new MessageReceiver();
receiver.receiveAndProcess(queue, ((EntityManagerFactory)env.get(EnvironmentName.ENTITY_MANAGER_FACTORY)));
// validate if everything is stored in db
AuditLogService logService = new JPAAuditLogService(env);
List<ProcessInstanceLog> processInstances = logService.findProcessInstances("com.sample.ruleflow3");
assertEquals(1, processInstances.size());
List<NodeInstanceLog> nodeInstances = logService.findNodeInstances(processInstance.getId());
assertEquals(6, nodeInstances.size());
for (NodeInstanceLog nodeInstance: nodeInstances) {
assertEquals(processInstance.getId(), nodeInstance.getProcessInstanceId().longValue());
assertEquals("com.sample.ruleflow3", nodeInstance.getProcessId());
assertNotNull(nodeInstance.getDate());
}
//verify variables
List<VariableInstanceLog> variables = logService.findVariableInstances(processInstance.getId());
assertNotNull(variables);
assertEquals(2, variables.size());
VariableInstanceLog var = variables.get(0);
// initial value from rule flow definition
assertEquals("InitialValue", var.getValue());
assertEquals("", var.getOldValue());
assertEquals(processInstance.getId(), var.getProcessInstanceId().longValue());
assertEquals(processInstance.getProcessId(), var.getProcessId());
assertEquals("s", var.getVariableId());
assertEquals("s", var.getVariableInstanceId());
// value given at process start
var = variables.get(1);
// initial value from rule flow definition
assertEquals("test value", var.getValue());
assertEquals("InitialValue", var.getOldValue());
assertEquals(processInstance.getId(), var.getProcessInstanceId().longValue());
assertEquals(processInstance.getProcessId(), var.getProcessId());
assertEquals("s", var.getVariableId());
assertEquals("s", var.getVariableInstanceId());
logService.clear();
processInstances = logService.findProcessInstances("com.sample.ruleflow3");
logService.dispose();
assertTrue(processInstances.isEmpty());
}
@Test
public void testAsyncAuditLoggerCompleteWithVariablesCustomIndexer() throws Exception {
Environment env = createEnvironment(context);
// load the process
KnowledgeBase kbase = createKnowledgeBase();
// create a new session
StatefulKnowledgeSession session = createSession(kbase, env);
Map<String, Object> jmsProps = new HashMap<String, Object>();
jmsProps.put("jbpm.audit.jms.transacted", false);
jmsProps.put("jbpm.audit.jms.connection.factory", factory);
jmsProps.put("jbpm.audit.jms.queue", queue);
AbstractAuditLogger logger = AuditLoggerFactory.newInstance(Type.JMS, session, jmsProps);
assertNotNull(logger);
assertTrue((logger instanceof AsyncAuditLogProducer));
List<String> names = new LinkedList<String>();
names.add("john");
names.add("mary");
names.add("peter");
Map<String, Object> params = new HashMap<String, Object>();
params.put("list", names);
// start process instance
ProcessInstance processInstance = session.startProcess("com.sample.ruleflow3", params);
MessageReceiver receiver = new MessageReceiver();
receiver.receiveAndProcess(queue, ((EntityManagerFactory)env.get(EnvironmentName.ENTITY_MANAGER_FACTORY)));
// validate if everything is stored in db
AuditLogService logService = new JPAAuditLogService(env);
List<ProcessInstanceLog> processInstances = logService.findProcessInstances("com.sample.ruleflow3");
assertEquals(1, processInstances.size());
List<NodeInstanceLog> nodeInstances = logService.findNodeInstances(processInstance.getId());
assertEquals(12, nodeInstances.size());
for (NodeInstanceLog nodeInstance: nodeInstances) {
assertEquals(processInstance.getId(), nodeInstance.getProcessInstanceId().longValue());
assertEquals("com.sample.ruleflow3", nodeInstance.getProcessId());
assertNotNull(nodeInstance.getDate());
}
//verify variables
List<VariableInstanceLog> variables = logService.findVariableInstances(processInstance.getId());
assertNotNull(variables);
assertEquals(8, variables.size());
List<VariableInstanceLog> listVariables = new ArrayList<VariableInstanceLog>();
// collect only those that are related to list process variable
for (VariableInstanceLog v : variables) {
if (v.getVariableInstanceId().equals("list")) {
listVariables.add(v);
}
}
assertEquals(3, listVariables.size());
VariableInstanceLog var = listVariables.get(0);
assertEquals("john", var.getValue());
assertEquals("", var.getOldValue());
assertEquals(processInstance.getId(), var.getProcessInstanceId().longValue());
assertEquals(processInstance.getProcessId(), var.getProcessId());
assertEquals("list[0]", var.getVariableId());
assertEquals("list", var.getVariableInstanceId());
var = listVariables.get(1);
assertEquals("mary", var.getValue());
assertEquals("", var.getOldValue());
assertEquals(processInstance.getId(), var.getProcessInstanceId().longValue());
assertEquals(processInstance.getProcessId(), var.getProcessId());
assertEquals("list[1]", var.getVariableId());
assertEquals("list", var.getVariableInstanceId());
var = listVariables.get(2);
assertEquals("peter", var.getValue());
assertEquals("", var.getOldValue());
assertEquals(processInstance.getId(), var.getProcessInstanceId().longValue());
assertEquals(processInstance.getProcessId(), var.getProcessId());
assertEquals("list[2]", var.getVariableId());
assertEquals("list", var.getVariableInstanceId());
logService.clear();
processInstances = logService.findProcessInstances("com.sample.ruleflow3");
logService.dispose();
assertTrue(processInstances.isEmpty());
}
public StatefulKnowledgeSession createSession(KnowledgeBase kbase, Environment env) {
StatefulKnowledgeSession session = createKieSession(kbase, env);
session.getWorkItemManager().registerWorkItemHandler("Human Task", new SystemOutWorkItemHandler());
return session;
}
private void startHornetQServer() throws Exception {
jmsServer = new EmbeddedJMS();
jmsServer.start();
logger.debug("Started Embedded JMS Server");
BitronixHornetQXAConnectionFactory.connectionFactory = (XAConnectionFactory) jmsServer.lookup("ConnectionFactory");
PoolingConnectionFactory myConnectionFactory = new PoolingConnectionFactory ();
myConnectionFactory.setClassName("org.jbpm.process.audit.jms.BitronixHornetQXAConnectionFactory");
myConnectionFactory.setUniqueName("hornet");
myConnectionFactory.setMaxPoolSize(5);
myConnectionFactory.setAllowLocalTransactions(true);
myConnectionFactory.init();
factory = myConnectionFactory;
queue = (Queue) jmsServer.lookup("/queue/exampleQueue");
}
private void stopHornetQServer() throws Exception {
((PoolingConnectionFactory) factory).close();
jmsServer.stop();
jmsServer = null;
}
private class MessageReceiver {
void receiveAndProcess(Queue queue, EntityManagerFactory entityManagerFactory) throws Exception {
Connection qconnetion = factory.createConnection();
Session qsession = qconnetion.createSession(true, QueueSession.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = qsession.createConsumer(queue);
qconnetion.start();
AsyncAuditLogReceiver rec = new AsyncAuditLogReceiver(entityManagerFactory) {
@Override
public void onMessage(Message message) {
try {
// need to use transaction so entity manager will persist logs
UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction");
ut.begin();
super.onMessage(message);
ut.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
};
consumer.setMessageListener(rec);
// since we use message listener allow it to complete the async processing
Thread.sleep(2000);
consumer.close();
qsession.close();
qconnetion.close();
}
public List<Message> receive(Queue queue) throws Exception {
List<Message> messages = new ArrayList<Message>();
Connection qconnetion = factory.createConnection();
Session qsession = qconnetion.createSession(true, QueueSession.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = qsession.createConsumer(queue);
qconnetion.start();
Message m = null;
while ((m = consumer.receiveNoWait()) != null) {
messages.add(m);
}
consumer.close();
qsession.close();
qconnetion.close();
return messages;
}
}
}