package sushi.query.bpmn; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import sushi.bpmn.element.AbstractBPMNElement; import sushi.bpmn.element.AttachableElement; import sushi.bpmn.element.BPMNEventType; import sushi.bpmn.element.BPMNIntermediateEvent; import sushi.esper.SushiStreamProcessingAdapter; import sushi.event.SushiEventType; import sushi.query.PatternQueryType; import sushi.query.SushiPatternQuery; import sushi.query.SushiQueryTypeEnum; /** * This class creates Esper-Queries to observe BPMN timer-events, like attached timer events or intermediate timer events. * @author micha */ public class TimerQueryFactory extends AbstractPatternQueryFactory { public TimerQueryFactory(PatternQueryGenerator patternQueryGenerator) { super(patternQueryGenerator); } /** * This method should generate a query for a single BPMN element that has an attached timer, * this query makes it possible to fire an event after a specified time interval. * @param element * @param catchingMonitorableElement * @return */ @Override protected SushiPatternQuery generateQuery(AbstractBPMNElement element, AbstractBPMNElement catchingMonitorableElement, SushiPatternQuery parentQuery) { if(element instanceof AttachableElement){ return generateAttachedTimerQuery(element, catchingMonitorableElement, parentQuery); } else if(element instanceof BPMNIntermediateEvent){ return generateInterMediateTimerQuery(element, catchingMonitorableElement, parentQuery); } else { System.err.println("Input element should be a attachable element for an TIMER-query!"); return null; } } private SushiPatternQuery generateAttachedTimerQuery(AbstractBPMNElement element, AbstractBPMNElement catchingMonitorableElement, SushiPatternQuery parentQuery) { StringBuilder sequencePatternQueryString = new StringBuilder(); sequencePatternQueryString.append("SELECT * FROM PATTERN [("); SushiPatternQuery query = new SushiPatternQuery(generateQueryName("Timer"), null, SushiQueryTypeEnum.LIVE, PatternQueryType.TIMER, Arrays.asList(element)); addQueryRelationship(parentQuery, query); //TODO: Hier die Timer-Query SushiPatternQuery subQuery = new StateTransitionQueryFactory(patternQueryGenerator).generateQuery(element, catchingMonitorableElement, query); sequencePatternQueryString.append("EVERY S0=" + subQuery.getTitle()); String timerEventTypeName = element.getMonitoringPoints().get(0).getEventType().getTypeName(); SushiEventType boundaryTimerEventType = addPatternEventTypeToEsper(timerEventTypeName + "BoundaryTimer"); sequencePatternQueryString.append(" " + EsperPatternOperators.SEQUENCE.operator + " EVERY S1=" + boundaryTimerEventType.getTypeName()); sequencePatternQueryString.append(") "); if(catchingMonitorableElement == null){ sequencePatternQueryString.append("]"); } else { sequencePatternQueryString.append(" " + EsperPatternOperators.XOR.operator + " EVERY C1="); sequencePatternQueryString.append(catchingMonitorableElement.getMonitoringPoints().get(0).getEventType().getTypeName()); sequencePatternQueryString.append("]"); } sequencePatternQueryString.append(" WHERE sushi.esper.SushiUtils.isIntersectionNotEmpty({S0.ProcessInstances, S1.ProcessInstances})"); String queryString = sequencePatternQueryString.toString(); query.setQueryString(queryString); System.out.println(query.getTitle() + ": " + queryString); registerQuery(query); if(query != null && query.getListener() != null){ query.getListener().setCatchingElement(catchingMonitorableElement); } //Wenn, die SubQuery (StateTransition der Aktivität) gefeuert hat, soll der Timer ausgelöst werden SushiEventType attachableElementEventType = element.getMonitoringPoints().get(0).getEventType(); subQuery.getListener().setTimer(attachableElementEventType.getTypeName(), boundaryTimerEventType, ((AttachableElement) element).getAttachedIntermediateEvent().getTimeDuration()); return query; } private SushiPatternQuery generateInterMediateTimerQuery(AbstractBPMNElement element, AbstractBPMNElement catchingMonitorableElement, SushiPatternQuery parentQuery) { BPMNIntermediateEvent intermediateEvent = (BPMNIntermediateEvent) element; SushiPatternQuery timerQuery = null; if(intermediateEvent.getIntermediateEventType().equals(BPMNEventType.Timer)){ StringBuilder sequencePatternQueryString = new StringBuilder(); //SELECT * FROM PATTERN [((EVERY VOR1 OR VOR2 ... ) -> Timer)] timerQuery = new SushiPatternQuery(generateQueryName("Timer"), null, SushiQueryTypeEnum.LIVE, PatternQueryType.TIMER, Arrays.asList(element)); addQueryRelationship(parentQuery, timerQuery); //Nach Vorgängern mit MonitoringPoints suchen Set<AbstractBPMNElement> monitorablePredecessors = new HashSet<AbstractBPMNElement>(); Set<AbstractBPMNElement> visitedpredecessors = new HashSet<AbstractBPMNElement>(); getNearestMonitorablePredecessors(intermediateEvent, monitorablePredecessors, visitedpredecessors); List<SushiPatternQuery> predecessorQueries = new ArrayList<SushiPatternQuery>(findStateTransitionQueriesForElements(monitorablePredecessors)); if(!predecessorQueries.isEmpty()){ sequencePatternQueryString.append("SELECT * FROM PATTERN [("); //Für alle Vorgänger eine OR-Query erzeugen, sodass jeder der Vorgänger den Timer triggern kann SushiPatternQuery predecessorOrQuery = createPredecessorORQuery(predecessorQueries); sequencePatternQueryString.append("EVERY S1=" + predecessorOrQuery.getTitle() + " "); SushiEventType interMediateTimerEventType = addPatternEventTypeToEsper(intermediateEvent.getName() + "Timer"); sequencePatternQueryString.append(EsperPatternOperators.SEQUENCE.operator + " EVERY S2=" + interMediateTimerEventType.getTypeName() + ")"); if(catchingMonitorableElement == null){ sequencePatternQueryString.append("]"); } else { sequencePatternQueryString.append(" " + EsperPatternOperators.XOR.operator + " EVERY C1="); sequencePatternQueryString.append(catchingMonitorableElement.getMonitoringPoints().get(0).getEventType().getTypeName()); sequencePatternQueryString.append("]"); } //Gleiche Prozessinstanzbedingung anhängen sequencePatternQueryString.append(" WHERE sushi.esper.SushiUtils.isIntersectionNotEmpty({S1.ProcessInstances, S2.ProcessInstances})"); String queryString = sequencePatternQueryString.toString(); timerQuery.setQueryString(queryString); System.out.println(timerQuery.getTitle() + ": " + queryString); registerQuery(timerQuery); if(timerQuery != null && timerQuery.getListener() != null){ timerQuery.getListener().setCatchingElement(catchingMonitorableElement); } //Wenn die beobachtbaren Vorgängerqueries (StateTransition der Elemente) gefeuert haben, soll der Timer ausgelöst werden for(SushiPatternQuery predecessorQuery : predecessorQueries){ AbstractBPMNElement timerTrigger = predecessorQuery.getMonitoredElements().get(0); SushiEventType timmerTriggerEventType = timerTrigger.getMonitoringPoints().get(0).getEventType(); predecessorQuery.getListener().setTimer(timmerTriggerEventType.getTypeName(), interMediateTimerEventType, intermediateEvent.getTimeDuration()); } } } return timerQuery; } private SushiPatternQuery createPredecessorORQuery(List<SushiPatternQuery> predecessorQueries){ SushiPatternQuery predecessorOrQuery = new SushiPatternQuery(generateQueryName("TimerPredecessor"), null, SushiQueryTypeEnum.LIVE, PatternQueryType.XOR, new ArrayList<AbstractBPMNElement>()); StringBuilder sequencePatternQueryString = new StringBuilder(); for(int i = 0; i < predecessorQueries.size(); i++){ SushiPatternQuery predecessorQuery = predecessorQueries.get(i); sequencePatternQueryString.append("SELECT * FROM PATTERN [("); if(i < predecessorQueries.size() - 1){ sequencePatternQueryString.append("EVERY S" + i + "=" + predecessorQuery.getTitle() + EsperPatternOperators.XOR.operator); } else { //Letztes Element, daher kein OR-Operator mehr sequencePatternQueryString.append("EVERY S" + i + "=" + predecessorQuery.getTitle()); } } sequencePatternQueryString.append(")]"); predecessorOrQuery.setQueryString(sequencePatternQueryString.toString()); addPatternEventTypeToEsper(predecessorOrQuery.getTitle()); predecessorOrQuery.setListener(predecessorOrQuery.addToEsper(SushiStreamProcessingAdapter.getInstance())); return predecessorOrQuery; } private Set<SushiPatternQuery> findStateTransitionQueriesForElements(Set<AbstractBPMNElement> elements) { Set<SushiPatternQuery> matchingQueries = new HashSet<SushiPatternQuery>(); for(AbstractBPMNElement element : elements){ SushiPatternQuery query = this.patternQueryGenerator.getQueryForElement(element); if(query != null){ matchingQueries.add(query); } } return matchingQueries; } /** * Searches for all monitorable predecessors of the specified element and adds them to monitorablePredecessors, but only the nearest on every path. * @param element * @param monitorablePredecessors * @param visitedpredecessors */ private void getNearestMonitorablePredecessors(AbstractBPMNElement element, Set<AbstractBPMNElement> monitorablePredecessors, Set<AbstractBPMNElement> visitedpredecessors) { if(!visitedpredecessors.contains(element)){ visitedpredecessors.add(element); for(AbstractBPMNElement predecessor : element.getPredecessors()){ if(predecessor.hasMonitoringPointsWithEventType()){ monitorablePredecessors.add(predecessor); } else { getNearestMonitorablePredecessors(predecessor, monitorablePredecessors, visitedpredecessors); } } } } }