package won.bot.impl; import won.bot.framework.bot.base.EventBot; import won.bot.framework.bot.context.ParticipantCoordinatorBotContextWrapper; import won.bot.framework.eventbot.EventListenerContext; import won.bot.framework.eventbot.action.BaseEventBotAction; import won.bot.framework.eventbot.action.impl.wonmessage.ConnectFromListToListAction; import won.bot.framework.eventbot.action.impl.needlifecycle.CreateNeedWithFacetsAction; import won.bot.framework.eventbot.action.impl.needlifecycle.DeactivateAllNeedsOfListAction; import won.bot.framework.eventbot.action.impl.lifecycle.SignalWorkDoneAction; import won.bot.framework.eventbot.bus.EventBus; import won.bot.framework.eventbot.event.Event; import won.bot.framework.eventbot.event.impl.lifecycle.ActEvent; import won.bot.framework.eventbot.event.impl.listener.FinishedEvent; import won.bot.framework.eventbot.event.impl.needlifecycle.NeedCreatedEvent; import won.bot.framework.eventbot.event.impl.wonmessage.CloseFromOtherNeedEvent; import won.bot.framework.eventbot.event.impl.wonmessage.ConnectFromOtherNeedEvent; import won.bot.framework.eventbot.event.impl.wonmessage.MessageFromOtherNeedEvent; import won.bot.framework.eventbot.event.impl.wonmessage.OpenFromOtherNeedEvent; import won.bot.framework.eventbot.filter.impl.AcceptOnceFilter; import won.bot.framework.eventbot.filter.impl.FinishedEventFilter; import won.bot.framework.eventbot.filter.impl.OrFilter; import won.bot.framework.eventbot.listener.*; import won.bot.framework.eventbot.listener.EventListener; import won.bot.framework.eventbot.listener.baStateBots.BATestBotScript; import won.bot.framework.eventbot.listener.baStateBots.BATestScriptListener; import won.bot.framework.eventbot.listener.baStateBots.baCCMessagingBots.atomicBots.SecondPhaseStartedEvent; import won.bot.framework.eventbot.listener.impl.ActionOnEventListener; import won.bot.framework.eventbot.listener.impl.ActionOnceAfterNEventsListener; import won.protocol.model.FacetType; import java.net.URI; import java.util.*; /** * User: Danijel * Date: 10.4.14. */ public abstract class BAAtomicBaseBot extends EventBot{ protected final int noOfNeeds; protected final List<TwoPhaseScript> scripts; private static final long MILLIS_BETWEEN_MESSAGES = 10; protected BaseEventListener participantNeedCreator; protected BaseEventListener coordinatorNeedCreator; protected BaseEventListener needConnector; protected BaseEventListener scriptsDoneListener; protected BaseEventListener firstPhaseDoneListener; protected BaseEventListener workDoneSignaller; protected final List<TwoPhaseScriptListener> scriptListeners; protected BaseEventListener firstPhaseCompleteListener; private Object scriptIteratorMonitor = new Object(); protected BAAtomicBaseBot() { this.scripts = setupScripts(); this.noOfNeeds = scripts.size()+1; this.scriptListeners = Collections.synchronizedList( new ArrayList <TwoPhaseScriptListener>(noOfNeeds -1)); } /** * Fetches scripts for first and second phase from concrete implementation and builds one list of TwoPhaseScript * objects. * @return */ private List<TwoPhaseScript> setupScripts(){ List<BATestBotScript> firstPhaseScripts = getFirstPhaseScripts(); List<BATestBotScript> secondPhaseScripts = getSecondPhaseScripts(); if (secondPhaseScripts.size() != firstPhaseScripts.size()) { throw new IllegalArgumentException("The same number of scripts in first and second phase is required!"); } List<TwoPhaseScript> scripts = new LinkedList<TwoPhaseScript>(); Iterator<BATestBotScript> firstIter = firstPhaseScripts.iterator(); Iterator<BATestBotScript> secondIter = secondPhaseScripts.iterator(); while(firstIter.hasNext()){ TwoPhaseScript script = new TwoPhaseScript(firstIter.next(), secondIter.next()); scripts.add(script); } return Collections.synchronizedList(scripts); } protected abstract FacetType getParticipantFacetType(); protected abstract FacetType getCoordinatorFacetType(); @Override protected void initializeEventListeners() { final EventListenerContext ctx = getEventListenerContext(); final EventBus bus = getEventBus(); ParticipantCoordinatorBotContextWrapper botContextWrapper = (ParticipantCoordinatorBotContextWrapper) getBotContextWrapper(); //create needs every trigger execution until noOfNeeds are created this.participantNeedCreator = new ActionOnEventListener( ctx, "participantCreator", new CreateNeedWithFacetsAction(ctx, botContextWrapper.getParticipantListName(), getParticipantFacetType().getURI()), noOfNeeds - 1 ); bus.subscribe(ActEvent.class, this.participantNeedCreator); //when done, create one coordinator need this.coordinatorNeedCreator = new ActionOnEventListener( ctx, "coordinatorCreator", new FinishedEventFilter(participantNeedCreator), new CreateNeedWithFacetsAction(ctx, botContextWrapper.getCoordinatorListName(), getCoordinatorFacetType().getURI()), 1 ); bus.subscribe(FinishedEvent.class, this.coordinatorNeedCreator); FinishedEventFilter coordinatorCreatorFilter = new FinishedEventFilter(coordinatorNeedCreator); final Iterator<TwoPhaseScript> scriptIterator = scripts.iterator(); //make a composite filter, with one filter for each testScriptListener that wait // for the FinishedEvents the they emit. That filter will be used to shut // down all needs after all the scriptListeners have finished. final OrFilter firstPhaseScriptListenerFilter = new OrFilter(); final OrFilter secondPhaseScriptListenerFilter = new OrFilter(); //create a callback that gets called immediately before the connection is established ConnectFromListToListAction.ConnectHook scriptConnectHook = new ConnectFromListToListAction.ConnectHook() { @Override public void onConnect(final URI fromNeedURI, final URI toNeedURI) { TwoPhaseScript script = scriptIterator.next(); //create the listener that will execute the script actions BATestScriptListener testScriptListener = new BATestScriptListener(ctx, script.getFirstPhaseScript(), fromNeedURI, toNeedURI, MILLIS_BETWEEN_MESSAGES); //subscribe it to the relevant events. bus.subscribe(ConnectFromOtherNeedEvent.class, testScriptListener); bus.subscribe(OpenFromOtherNeedEvent.class, testScriptListener); bus.subscribe(MessageFromOtherNeedEvent.class, testScriptListener); //add a filter that will wait for the FinishedEvent emitted by that listener //wrap it in an acceptance filter to make extra sure we count each listener only once. firstPhaseScriptListenerFilter.addFilter( new AcceptOnceFilter( new FinishedEventFilter(testScriptListener))); //now we create the listener that is only active in the second phase //remember it so we can check its state later BATestScriptListener secondPhaseTestScriptListener = new BATestScriptListener(ctx, script.getSecondPhaseScript(),fromNeedURI,toNeedURI, MILLIS_BETWEEN_MESSAGES); //remember both listeners as a pair as we'll need them together later TwoPhaseScriptListener twoPhaseScriptListener = new TwoPhaseScriptListener(testScriptListener, secondPhaseTestScriptListener); scriptListeners.add(twoPhaseScriptListener); secondPhaseScriptListenerFilter.addFilter( new AcceptOnceFilter( new FinishedEventFilter(secondPhaseTestScriptListener))); } }; //when done, connect the participants to the coordinator this.needConnector = new ActionOnceAfterNEventsListener( ctx, "needConnector", noOfNeeds, new ConnectFromListToListAction(ctx, botContextWrapper.getCoordinatorListName(), botContextWrapper.getParticipantListName(), getCoordinatorFacetType().getURI(), getParticipantFacetType().getURI(), MILLIS_BETWEEN_MESSAGES, scriptConnectHook,"Hi!")); bus.subscribe(NeedCreatedEvent.class, this.needConnector); //for each group member, there are 2 listeners waiting for messages. when they are all finished, we're done. this.firstPhaseDoneListener = new ActionOnceAfterNEventsListener( ctx, "firstPhaseDoneListener", firstPhaseScriptListenerFilter, noOfNeeds - 1, new BaseEventBotAction(ctx) { @Override protected void doRun(final Event event, EventListener executingListener) throws Exception { logger.debug("starting second phase"); for(TwoPhaseScriptListener listener: scriptListeners){ logger.debug("subscribing second phase listener {}", listener); //subscribe it to the relevant events. bus.subscribe(MessageFromOtherNeedEvent.class, listener.getSecondPhaseListener()); bus.subscribe(SecondPhaseStartedEvent.class, listener.getSecondPhaseListener()); listener.getSecondPhaseListener().setCoordinatorSideConnectionURI(listener.getFirstPhaseListener() .getCoordinatorSideConnectionURI()); listener.getSecondPhaseListener().setParticipantSideConnectionURI(listener.getFirstPhaseListener() .getParticipantSideConnectionURI()); listener.getSecondPhaseListener().updateFilterForBothConnectionURIs(); bus.publish(new SecondPhaseStartedEvent( listener.getFirstPhaseListener().getCoordinatorURI(), listener.getFirstPhaseListener().getCoordinatorSideConnectionURI(), listener.getFirstPhaseListener().getParticipantURI())); } } }); bus.subscribe(FinishedEvent.class, this.firstPhaseDoneListener); //for each group member, there are 2 listeners waiting for messages. when they are all finished, we're done. this.scriptsDoneListener = new ActionOnceAfterNEventsListener( ctx, "scriptsDoneListener", secondPhaseScriptListenerFilter, noOfNeeds - 1, new DeactivateAllNeedsOfListAction(ctx, botContextWrapper.getParticipantListName())); bus.subscribe(FinishedEvent.class, this.scriptsDoneListener); //When the needs are deactivated, all connections are closed. wait for the close events and signal work done. this.workDoneSignaller = new ActionOnceAfterNEventsListener( ctx, "workDoneSignaller", noOfNeeds - 1, new SignalWorkDoneAction(ctx) ); bus.subscribe(CloseFromOtherNeedEvent.class, this.workDoneSignaller); } protected abstract List<BATestBotScript> getFirstPhaseScripts(); protected abstract List<BATestBotScript> getSecondPhaseScripts(); private class TwoPhaseScript{ private BATestBotScript firstPhaseScript; private BATestBotScript secondPhaseScript; private TwoPhaseScript(final BATestBotScript firstPhaseScript, final BATestBotScript secondPhaseScript) { this.firstPhaseScript = firstPhaseScript; this.secondPhaseScript = secondPhaseScript; } public BATestBotScript getSecondPhaseScript() { return secondPhaseScript; } public BATestBotScript getFirstPhaseScript() { return firstPhaseScript; } } private class TwoPhaseScriptListener { private BATestScriptListener firstPhaseListener; private BATestScriptListener secondPhaseListener; private TwoPhaseScriptListener(final BATestScriptListener firstPhaseListener, final BATestScriptListener secondPhaseListener) { this.firstPhaseListener = firstPhaseListener; this.secondPhaseListener = secondPhaseListener; } public BATestScriptListener getFirstPhaseListener() { return firstPhaseListener; } public BATestScriptListener getSecondPhaseListener() { return secondPhaseListener; } } }