package sushi.correlation; import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import sushi.event.SushiEvent; import sushi.event.SushiEventType; import sushi.event.attribute.SushiAttribute; import sushi.event.collection.SushiMapTree; import sushi.process.SushiProcess; import sushi.process.SushiProcessInstance; /** * Provides methods to correlate existing and incoming events to process instances using correlation rules. */ public class RuleCorrelator { /** * Defines the correlation for a process using correlation rules and correlates existing events for the process. * * @param correlationRules set of correlation rules (pairs of attributes belonging to the same or different event type) * defining the correlation of the given process (e.g. E1.A=E2.B, E2.G=E3.H, E3.H=E4.H for event types E1, E2, E3, E4) * @param process the process from which the process instances are derived and created * @param timeCondition (optional) rule for advanced time correlation related to the process */ public static void correlate(Set<CorrelationRule> correlationRules, SushiProcess process, TimeCondition timeCondition) { Set<SushiEventType> eventTypes = new HashSet<SushiEventType>(); for (CorrelationRule rule : correlationRules) { process.addCorrelationRule(rule); eventTypes.add(rule.getFirstAttribute().getEventType()); eventTypes.add(rule.getSecondAttribute().getEventType()); } if (timeCondition != null) { timeCondition.save(); process.setTimeCondition(timeCondition); } process.setEventTypes(eventTypes); process.merge(); Set<SushiEvent> eventsToCorrelate = new HashSet<SushiEvent>(); for(SushiEventType actualEventType : eventTypes){ eventsToCorrelate.addAll(SushiEvent.findByEventType(actualEventType)); } Iterator<SushiEvent> eventIterator = eventsToCorrelate.iterator(); while(eventIterator.hasNext()){ SushiEvent actualEvent = eventIterator.next(); correlateEventToProcessInstance(actualEvent, correlationRules, process, timeCondition); } } /** * Correlates an event to a process instance using correlation rules. * If no matching process instance is found, a new process instance is created and the event is be correlated to this instance. * * @param actualEvent the event to be correlated to a process instance * @param correlationRules set of correlation rules (pairs of attributes belonging to the same or different event type) * defining the correlation of the given process (e.g. E1.A=E2.B, E2.G=E3.H, E3.H=E4.H for event types E1, E2, E3, E4) * @param process the process from which the process instances are derived and created * @param timeCondition (optional) rule for advanced time correlation related to the process */ static void correlateEventToProcessInstance(SushiEvent actualEvent, Set<CorrelationRule> correlationRules, SushiProcess process, TimeCondition timeCondition) { boolean insertedInExistingProcessInstance = false; List<SushiProcessInstance> processInstances = SushiProcessInstance.findByProcess(process); Set<SushiProcessInstance> matchedProcessInstances = new HashSet<SushiProcessInstance>(); /* * Looking for matching existing process instances. * If no rule for advanced time correlation is provided, the event is related to a process instance * if their values defined through the correlation rules are equal. */ for (SushiProcessInstance actualProcessInstance : processInstances) { boolean processInstanceAndEventMatch = false; SushiMapTree<String, Serializable> valueTreeOfProcessInstance = actualProcessInstance.getCorrelationAttributesAndValues(); SushiMapTree<String, Serializable> valueTreeOfEvent = actualEvent.getValues(); for (CorrelationRule actualCorrelationRule : correlationRules) { if (actualCorrelationRule.getFirstAttribute().getEventType().equals(actualEvent.getEventType())) { String qualifiedAttributeName = actualCorrelationRule.getSecondAttribute().getQualifiedAttributeName(); String attributeExpression = actualCorrelationRule.getFirstAttribute().getAttributeExpression(); if (valueTreeOfProcessInstance.get(qualifiedAttributeName) != null) { if (valueTreeOfProcessInstance.get(qualifiedAttributeName).toString().equals(valueTreeOfEvent.get(attributeExpression).toString())) { processInstanceAndEventMatch = true; } else { processInstanceAndEventMatch = false; break; } continue; } } if (actualCorrelationRule.getSecondAttribute().getEventType().equals(actualEvent.getEventType())) { String qualifiedAttributeName = actualCorrelationRule.getFirstAttribute().getQualifiedAttributeName(); String attributeExpression = actualCorrelationRule.getSecondAttribute().getAttributeExpression(); if (valueTreeOfProcessInstance.get(qualifiedAttributeName) != null) { if (valueTreeOfProcessInstance.get(qualifiedAttributeName).toString().equals(valueTreeOfEvent.get(attributeExpression).toString())) { processInstanceAndEventMatch = true; } else { processInstanceAndEventMatch = false; break; } continue; } } } if (processInstanceAndEventMatch) { matchedProcessInstances.add(actualProcessInstance); } } /* * If the event matches to exactly one process instance: * If no rule for advanced time correlation is provided, * the event is added to the process instance. * If a rule for advanced time correlation is provided, * the event must additionally belong to the time period defined in the rule for advanced time correlation. * * If the event matches to more than one process instance: * If no rule for advanced time correlation is provided, * the process instances are merged to one process instance and the event is added to the process instance. * If a rule for advanced time correlation is provided, * the event must additionally belong to the time period defined in the rule for advanced time correlation. * The process instances will be merged only if the timer events are equal. * * The event is finally added to the process instance(s). */ if (!matchedProcessInstances.isEmpty()) { for (SushiProcessInstance actualProcessInstance : matchedProcessInstances) { if (timeCondition == null || timeCondition.belongsEventToTimerEvent(actualEvent, actualProcessInstance.getTimerEvent())) { Iterator<SushiProcessInstance> processInstanceIterator = matchedProcessInstances.iterator(); while (processInstanceIterator.hasNext()) { SushiProcessInstance processInstanceToMerge = processInstanceIterator.next(); if (processInstanceToMerge != actualProcessInstance && (processInstanceToMerge.getTimerEvent() == null || processInstanceToMerge.getTimerEvent().equals(actualProcessInstance.getTimerEvent()))) { for (SushiEvent eventToMerge : processInstanceToMerge.getEvents()) { if (!actualProcessInstance.getEvents().contains(eventToMerge)) { actualProcessInstance.addEvent(eventToMerge); eventToMerge.addProcessInstance(actualProcessInstance); eventToMerge.save(); } } // The correlation values are merged here. actualProcessInstance.getCorrelationAttributesAndValues().putAll(processInstanceToMerge.getCorrelationAttributesAndValues()); processInstanceToMerge.remove(); System.out.println("Process instance merged and removed."); } } storeCorrelationValuesOfProcessInstance(actualProcessInstance, correlationRules, actualEvent); actualProcessInstance.addEvent(actualEvent); actualProcessInstance.merge(); actualEvent.addProcessInstance(actualProcessInstance); actualEvent.merge(); insertedInExistingProcessInstance = true; break; } } } /* * If no process instance is matched and no rule for advanced time correlation is defined, * a new process instance is created here, the correlation values are taken from the event and * stored in the process instance. The event is finally added to the new process instance. * If no process instance is matched and a rule for advanced time correlation is defined, * a new process instance is created here only if a timer event serving as the benchmark * to which the advanced time correlation can be related exists and is not already * related to a process instance. */ if (!insertedInExistingProcessInstance) { SushiProcessInstance newProcessInstance = new SushiProcessInstance(); if (timeCondition != null) { SushiEvent timerEvent = timeCondition.getTimerEventForEvent(actualEvent, correlationRules); if (timerEvent == null) { return; } else { newProcessInstance.setTimerEvent(timerEvent); } } storeCorrelationValuesOfProcessInstance(newProcessInstance, correlationRules, actualEvent); newProcessInstance.addEvent(actualEvent); newProcessInstance.save(); actualEvent.addProcessInstance(newProcessInstance); actualEvent.merge(); process.addProcessInstance(newProcessInstance); process.save(); System.out.println("New process instance added."); } } /** * Helper method to store correlation values in a given process instance based on the new event. * May cascade over the given correlation rules. Example: Event from type E2 has values c=3 and d=4. * Correlation rules are E1.a=E2.c and E2.d=E3.d - correlation values are. E1.a=3, E2.c=3, E2.d=4 and E3.d=4. * * @param processInstance the process instance where the correlation values have to be stored * @param correlationRules set of correlation rules defining the correlation of the given process * @param event the event where the values for correlation are derived from */ private static void storeCorrelationValuesOfProcessInstance(SushiProcessInstance processInstance, Set<CorrelationRule> correlationRules, SushiEvent event) { List<SushiAttribute> correlationAttributes = extractCorrelationAttributes(correlationRules); // Set<CorrelationRule> correlationRules = new HashSet<CorrelationRule>(correlationRulesOfProcess); for (SushiAttribute actualCorrelationAttribute : correlationAttributes) { if (actualCorrelationAttribute.getEventType().equals(event.getEventType())) { String qualifiedAttributeName = actualCorrelationAttribute.getQualifiedAttributeName(); String attributeExpression = actualCorrelationAttribute.getAttributeExpression(); Serializable correlationValue = event.getValues().get(attributeExpression); if (!processInstance.getCorrelationAttributesAndValues().containsKey(qualifiedAttributeName)) { processInstance.getCorrelationAttributesAndValues().put(qualifiedAttributeName, correlationValue); // correlationAttributes.remove(actualCorrelationAttribute); // find related attribute for (CorrelationRule actualCorrelationRule : correlationRules) { if (actualCorrelationAttribute.equalsWithEventType(actualCorrelationRule.getFirstAttribute())) { SushiAttribute relatedAttribute = actualCorrelationRule.getSecondAttribute(); if (!processInstance.getCorrelationAttributesAndValues().containsKey(relatedAttribute.getQualifiedAttributeName())) { processInstance.getCorrelationAttributesAndValues().put(relatedAttribute.getQualifiedAttributeName(), correlationValue); // correlationAttributes.remove(relatedAttribute); // correlationRules.remove(actualCorrelationRule); storeCorrelationValuesOfProcessInstance(processInstance, correlationRules, correlationAttributes, relatedAttribute, correlationValue); } } if (actualCorrelationAttribute.equalsWithEventType(actualCorrelationRule.getSecondAttribute())) { SushiAttribute relatedAttribute = actualCorrelationRule.getFirstAttribute(); if (!processInstance.getCorrelationAttributesAndValues().containsKey(relatedAttribute.getQualifiedAttributeName())) { processInstance.getCorrelationAttributesAndValues().put(relatedAttribute.getQualifiedAttributeName(), correlationValue); // correlationAttributes.remove(relatedAttribute); // correlationRules.remove(actualCorrelationRule); storeCorrelationValuesOfProcessInstance(processInstance, correlationRules, correlationAttributes, relatedAttribute, correlationValue); } } } } } } } private static void storeCorrelationValuesOfProcessInstance(SushiProcessInstance processInstance, Set<CorrelationRule> correlationRules, List<SushiAttribute> correlationAttributes, SushiAttribute actualAttribute, Serializable correlationValue) { for (CorrelationRule actualCorrelationRule : correlationRules) { if (actualAttribute.equalsWithEventType(actualCorrelationRule.getFirstAttribute())) { SushiAttribute relatedAttribute = actualCorrelationRule.getSecondAttribute(); if (!processInstance.getCorrelationAttributesAndValues().containsKey(relatedAttribute.getQualifiedAttributeName())) { processInstance.getCorrelationAttributesAndValues().put(relatedAttribute.getQualifiedAttributeName(), correlationValue); storeCorrelationValuesOfProcessInstance(processInstance, correlationRules, correlationAttributes, relatedAttribute, correlationValue); } } if (actualAttribute.equalsWithEventType(actualCorrelationRule.getSecondAttribute())) { SushiAttribute relatedAttribute = actualCorrelationRule.getFirstAttribute(); if (!processInstance.getCorrelationAttributesAndValues().containsKey(relatedAttribute.getQualifiedAttributeName())) { processInstance.getCorrelationAttributesAndValues().put(relatedAttribute.getQualifiedAttributeName(), correlationValue); storeCorrelationValuesOfProcessInstance(processInstance, correlationRules, correlationAttributes, relatedAttribute, correlationValue); } } } } /** * Helper method to extract all attributes from the correlation rules. Unique by attribute expression and event type. * * @param correlationRules correlation rules where the attributes have to be extracted from * @return list of event type attributes */ private static List<SushiAttribute> extractCorrelationAttributes(Set<CorrelationRule> correlationRules) { List<SushiAttribute> correlationAttributes = new ArrayList<SushiAttribute>(); for (CorrelationRule actualCorrelationRule : correlationRules) { boolean firstAttributeInList = false, secondAttributeInList = false; for (SushiAttribute correlationAttribute : correlationAttributes) { if (correlationAttribute.equalsWithEventType(actualCorrelationRule.getFirstAttribute())) { firstAttributeInList = true; } if (correlationAttribute.equalsWithEventType(actualCorrelationRule.getSecondAttribute())) { secondAttributeInList = true; } } if (!firstAttributeInList) { correlationAttributes.add(actualCorrelationRule.getFirstAttribute()); } if (!secondAttributeInList) { correlationAttributes.add(actualCorrelationRule.getSecondAttribute()); } } return correlationAttributes; } }