/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.processFlow.knowledgeService; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.util.Date; import javax.enterprise.inject.spi.Producer; import javax.jms.BytesMessage; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.Session; import org.drools.audit.event.LogEvent; import org.drools.event.process.DefaultProcessEventListener; import org.drools.event.process.ProcessCompletedEvent; import org.drools.event.process.ProcessNodeLeftEvent; import org.drools.event.process.ProcessNodeTriggeredEvent; import org.drools.event.process.ProcessStartedEvent; import org.drools.event.process.ProcessVariableChangedEvent; import org.jboss.marshalling.Marshaller; import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.Marshalling; import org.jboss.marshalling.MarshallingConfiguration; import org.jbpm.process.audit.NodeInstanceLog; import org.jbpm.process.audit.ProcessInstanceLog; import org.jbpm.process.audit.VariableInstanceLog; import org.jbpm.workflow.instance.node.HumanTaskNodeInstance; import org.jbpm.workflow.instance.node.SubProcessNodeInstance; import org.jboss.processFlow.bam.IBAMService; import org.jboss.processFlow.bam.SubProcessInstanceLog; import org.jboss.processFlow.bam.HumanTaskLog; import org.jboss.processFlow.util.MessagingUtil; public class MultiThreadedAsyncBAMProducer extends DefaultProcessEventListener { public static final String LOG_EVENT_TYPE = "logEventType"; private static final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("river"); private static final MarshallingConfiguration configuration = new MarshallingConfiguration(); private static Connection connectObj; private static Destination dQueue; public MultiThreadedAsyncBAMProducer() { try { ConnectionFactory cFactory = MessagingUtil.grabConnectionFactory(); connectObj = cFactory.createConnection(); dQueue = (Destination)MessagingUtil.grabDestination(IBAMService.BAM_QUEUE); } catch(Exception x) { throw new RuntimeException(x); } } @Override public void beforeProcessStarted(ProcessStartedEvent event) { try { ProcessInstanceLog log = new ProcessInstanceLog(event.getProcessInstance().getId(), event.getProcessInstance().getProcessId()); sendBAMEvent(LogEvent.BEFORE_RULEFLOW_CREATED, log); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void afterProcessCompleted(ProcessCompletedEvent event) { try { ProcessInstanceLog log = new ProcessInstanceLog(event.getProcessInstance().getId(), event.getProcessInstance().getProcessId()); // Nick: set the end date before pass it to BAM, to make sure the log date is consistent on the same server log.setEnd(new Date()); sendBAMEvent(LogEvent.AFTER_RULEFLOW_COMPLETED, log); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void beforeNodeTriggered(ProcessNodeTriggeredEvent event) { try { NodeInstanceLog log = new NodeInstanceLog( NodeInstanceLog.TYPE_ENTER, event.getProcessInstance().getId(), event.getProcessInstance().getProcessId(), String.valueOf(event.getNodeInstance().getId()), getuniqueNodeid(event.getNodeInstance().getNode()), event.getNodeInstance().getNodeName()); sendBAMEvent(LogEvent.BEFORE_RULEFLOW_NODE_TRIGGERED, log); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void afterNodeTriggered(ProcessNodeTriggeredEvent event) { try { if (event.getNodeInstance() instanceof SubProcessNodeInstance) { SubProcessInstanceLog log = new SubProcessInstanceLog(); log.setSubProcessInstanceId(((SubProcessNodeInstance) event.getNodeInstance()).getProcessInstanceId()); log.setParentProcessInstanceId(event.getProcessInstance().getId()); log.setSubProcessNodeInstanceId(event.getNodeInstance().getId()); sendBAMEvent(SubProcessInstanceLog.AFTER_SUBPROCESSINSTANCE_CREATED, log); } else if (event.getNodeInstance() instanceof HumanTaskNodeInstance) { HumanTaskLog log = new HumanTaskLog(); log.setWorkItemId(((HumanTaskNodeInstance) event.getNodeInstance()).getWorkItemId()); log.setNodeInstanceId(event.getNodeInstance().getId()); log.setProcessInstanceId(event.getProcessInstance().getId()); sendBAMEvent(HumanTaskLog.AFTER_HUMANTASK_CREATED, log); } } catch (Exception e) { throw new RuntimeException(e); } } @Override public void beforeNodeLeft(ProcessNodeLeftEvent event) { try { NodeInstanceLog log = new NodeInstanceLog( NodeInstanceLog.TYPE_EXIT, event.getProcessInstance().getId(), event.getProcessInstance().getProcessId(), String.valueOf(event.getNodeInstance().getId()), getuniqueNodeid(event.getNodeInstance().getNode()), event.getNodeInstance().getNodeName()); sendBAMEvent(LogEvent.BEFORE_RULEFLOW_NODE_EXITED, log); } catch (Exception e) { throw new RuntimeException(e); } } @Override public void afterVariableChanged(ProcessVariableChangedEvent event) { String objectToString = event.getNewValue() == null ? "null" : event.getNewValue().toString(); try { VariableInstanceLog log = new VariableInstanceLog(event.getProcessInstance().getId(), event.getProcessInstance().getProcessId(), event.getVariableInstanceId(), event.getVariableId(), objectToString); sendBAMEvent(LogEvent.AFTER_VARIABLE_INSTANCE_CHANGED, log); } catch (Exception e) { throw new RuntimeException(e); } } private void sendBAMEvent(int logEventType, Serializable logObj) throws JMSException, IOException { Session sessionObj = null; MessageProducer pObj = null; try { sessionObj = this.connectObj.createSession(false, Session.AUTO_ACKNOWLEDGE); pObj = sessionObj.createProducer(dQueue); BytesMessage bMessage = sessionObj.createBytesMessage(); if (logObj != null) { bMessage.writeBytes(marshall(logObj)); } bMessage.setIntProperty(LOG_EVENT_TYPE, logEventType); pObj.send(bMessage); }finally { if(pObj != null) pObj.close(); if(sessionObj != null) sessionObj.close(); } } private byte[] marshall(Object obj) throws IOException { final Marshaller marshaller = marshallerFactory.createMarshaller(configuration); final ByteArrayOutputStream os = new ByteArrayOutputStream(); try { marshaller.start(Marshalling.createByteOutput(os)); marshaller.writeObject(obj); marshaller.finish(); return os.toByteArray(); } finally { if (os != null) { os.close(); } } } /** * Get the unique id of this <code>node</code>, this method is different from {@link org.drools.definition.process.Node#getId()} * which returns the id scoped in its container node. * <p> * The unique id of this <code>node</code> is the internal id in form of <code>"_${containerId}_${nodeId}"</code> * </p> * <p> * Both the BAM producer and the consumer/audit-trail is expected to invoke this method to make sure the consistency. * </p> * * @param node * @return */ public static String getuniqueNodeid(org.drools.definition.process.Node node) { return org.jbpm.bpmn2.xml.XmlBPMNProcessDumper.getUniqueNodeId(node); } }