package sushi.query; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import sushi.bpmn.element.AbstractBPMNElement; import sushi.bpmn.element.AttachableElement; import sushi.esper.SushiStreamProcessingAdapter; import sushi.esper.SushiUtils; import sushi.event.SushiEvent; import sushi.event.SushiEventType; import sushi.event.attribute.SushiAttribute; import sushi.event.collection.SushiMapTree; import sushi.monitoring.bpmn.BPMNQueryMonitor; import sushi.process.SushiProcessInstance; import sushi.util.SetUtil; import com.espertech.esper.client.EventBean; import com.espertech.esper.event.xml.XMLEventBean; /** * A listener for {@link SushiPatternQuery}. * @author micha */ public class SushiPatternQueryListener extends SushiLiveQueryListener implements Serializable { private static final long serialVersionUID = 1L; private List<SushiEventType> loopBreakEventTypes; private boolean isLoopQueryListener = false; private List<Integer> alreadyTriggeredProcessInstances = new ArrayList<Integer>(); private AbstractBPMNElement catchingElement = null; private String timerTriggerEventTypeName = new String(); private float timeDuration = 0; private SushiEventType timerEventType; public SushiPatternQueryListener(SushiQuery patternQuery) { super(patternQuery); } public SushiPatternQueryListener(SushiQuery patternQuery, List<SushiEventType> loopBreakEventTypes) { super(patternQuery); this.loopBreakEventTypes = loopBreakEventTypes; this.isLoopQueryListener = true; } @Override public void update(EventBean[] newData, EventBean[] oldData) { if(newData[0].getUnderlying() instanceof HashMap){ //ProcessInstance für PatternEvent ermitteln List<Set<Integer>> processInstancesList = new ArrayList<Set<Integer>>(); HashMap patternEvent = (HashMap) newData[0].getUnderlying(); for(Object value : patternEvent.values()){ if(value instanceof XMLEventBean){ XMLEventBean bean = (XMLEventBean) value; if(bean.get("ProcessInstances") != null){ processInstancesList.add(new HashSet<Integer>((List<Integer>) bean.get("ProcessInstances"))); } } } System.err.println(query.getTitle() + ": " + processInstancesList); Set<Integer> processInstances = SetUtil.intersection(processInstancesList); if(!processInstances.isEmpty()){ /*TODO: processInstances.iterator().next() ist nicht ausreichend, hier würde nur die erste ProcessInstance berücksichtigt werden, * auch wenn das Event zu mehreren ProcessInstanzen gehört*/ BPMNQueryMonitor.getInstance().setQueryFinishedForProcessInstance((SushiPatternQuery) query, SushiProcessInstance.findByID(processInstances.iterator().next())); //Neues Event erzeugen und an Esper schicken SushiEvent event = new SushiEvent(SushiEventType.findByTypeName(query.getTitle()), new Date()); for(int processInstanceID : processInstances){ event.addProcessInstance(SushiProcessInstance.findByID(processInstanceID)); } if(event.getEventType() != null){ SushiStreamProcessingAdapter.getInstance().addEvent(event); } //Gefangene Events nochmal abschicken, die in anderer Query wieder gebraucht werden if(!SushiUtils.isIntersectionNotEmpty(alreadyTriggeredProcessInstances, new ArrayList<Integer>(processInstances))){ /*Wurde Event schon zum zweiten Mal abgeschickt? */ SushiEvent lastEvent = getLastEvent(newData); if(lastEvent != null){ //Schleife wurde getriggert if(isLoopQueryListener){ resendLastEvent(lastEvent); } //Catching-Event wurde getriggert if(catchingElement != null && lastEvent.getEventType().getTypeName().equals(catchingElement.getName())){ resendLastEvent(lastEvent); } //Timer-Event wurde getriggert if(!timerTriggerEventTypeName.isEmpty() && lastEvent.getEventType().getTypeName().equals(timerTriggerEventTypeName)){ TimerListener timerListener = new TimerListener(lastEvent, timerEventType, timeDuration); timerListener.start(); } } } } } System.out.println("Event received for query " + query.getTitle() + ": " + newData[0].getUnderlying()); } private SushiEvent getLastEvent(EventBean[] newData){ SushiEvent event = null; if(newData[0].getUnderlying() instanceof HashMap){ HashMap patternEvent = (HashMap) newData[0].getUnderlying(); for(Object value : patternEvent.values()){ if(value instanceof XMLEventBean){ XMLEventBean bean = (XMLEventBean) value; SushiEventType eventType = SushiEventType.findByTypeName(bean.getEventType().getName()); SushiMapTree<String, Serializable> values = new SushiMapTree<String, Serializable>(); for(SushiAttribute valueType : eventType.getValueTypes()){ values.put(valueType.getName(), (Serializable) bean.get(valueType.getName())); } event = new SushiEvent(eventType, new Date(), values); List<Integer> processInstanceIDs = (List<Integer>) bean.get("ProcessInstances"); alreadyTriggeredProcessInstances.addAll(processInstanceIDs); for(Integer processInstanceID : processInstanceIDs){ event.addProcessInstance(SushiProcessInstance.findByID(processInstanceID)); } } } } return event; } private void resendLastEvent(SushiEvent event) { //TODO: Nicht speichern, Event nur zu Esper senden SushiStreamProcessingAdapter.getInstance().addEvent(event); } public List<SushiEventType> getLoopBreakEventTypes() { return loopBreakEventTypes; } public void setLoopBreakEventTypes(List<SushiEventType> loopBreakEventTypes) { this.loopBreakEventTypes = loopBreakEventTypes; this.isLoopQueryListener = true; } public boolean isLoopQueryListener() { return isLoopQueryListener; } public void setLoopQueryListener(boolean isLoopQueryListener) { this.isLoopQueryListener = isLoopQueryListener; } public AbstractBPMNElement getCatchingElement() { return catchingElement; } public void setCatchingElement(AbstractBPMNElement catchingElement) { this.catchingElement = catchingElement; } /** * Adds a timerTrigger, that should be the name of the eventtype, that triggers the timer, to this query. * If the listener gets such a timerTrigger, it starts a timer thread, that fires the timerEventType after * the specified timeDuration to Esper. * @param timerTrigger * @param timerEventType * @param timeDuration */ public void setTimer(String timerTriggerEventTypeName, SushiEventType timerEventType, float timeDuration) { this.timerTriggerEventTypeName = timerTriggerEventTypeName; this.timerEventType = timerEventType; this.timeDuration = timeDuration; } }