package won.bot.impl;
import com.google.common.collect.Iterators;
import won.bot.framework.bot.base.EventBot;
import won.bot.framework.bot.context.AdditionalParticipantCoordinatorBotContextWrapper;
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.BaseEventListener;
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.bot.framework.eventbot.listener.impl.WaitForNEventsListener;
import won.protocol.model.FacetType;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* User: Danijel
* Date: 7.5.14.
*/
public abstract class BAAtomicAdditionalParticipantsBaseBot extends EventBot{
protected final int noOfNeeds;
protected final int noOfDelayedNeeds;
protected final int noOfNonDelayedNeeds;
protected final List<BATestBotScript> firstPhaseScripts;
protected final List<BATestBotScript> firstPhaseScriptsWithDelay;
protected final List<BATestBotScript> secondPhaseScripts;
// protected final List<BATestBotScript> secondPhaseScriptsWithDelay;
private static final long MILLIS_BETWEEN_MESSAGES = 10;
protected BaseEventListener participantNeedCreator;
protected BaseEventListener delayedParticipantNeedCreator;
protected BaseEventListener coordinatorNeedCreator;
protected BaseEventListener needConnector;
protected BaseEventListener needConnectorWithDelay;
protected BaseEventListener scriptsDoneListener;
protected BaseEventListener firstPhaseWithDelayDoneListener;
protected BaseEventListener workDoneSignaller;
protected final List<BATestScriptListener> firstPhasetestScriptListeners;
protected final List<BATestScriptListener> firstPhasetestScriptWithDelayListeners;
protected final List<BATestScriptListener> secondPhasetestScriptListeners;
// protected final List<BATestScriptListener> secondPhasetestScriptWithDelayListeners;
protected BaseEventListener firstPhaseCompleteListener;
protected BAAtomicAdditionalParticipantsBaseBot() {
this.firstPhaseScripts = getFirstPhaseScripts();
this.firstPhaseScriptsWithDelay = getFirstPhaseScriptsWithDelay();
this.secondPhaseScripts = getSecondPhaseScripts();
// this.secondPhaseScriptsWithDelay = getSecondPhaseScriptsWithDelay();
// if (this.secondPhaseScripts.size() != this.firstPhaseScripts.size()) {
// throw new IllegalArgumentException("The same number of scripts in first and second phase is required!");
// }
// if (this.secondPhaseScriptsWithDelay.size() != this.firstPhaseScriptsWithDelay.size())
// throw new IllegalArgumentException("The same number of scripts in first and second phase (wiht delay) is " +
// "required!");
this.noOfNonDelayedNeeds = firstPhaseScripts.size()+1;
this.noOfDelayedNeeds = firstPhaseScriptsWithDelay.size();
this.noOfNeeds = secondPhaseScripts.size()+1;
this.firstPhasetestScriptListeners = Collections.synchronizedList(new ArrayList<BATestScriptListener>
(noOfNonDelayedNeeds-1));
this.firstPhasetestScriptWithDelayListeners = Collections.synchronizedList(new ArrayList<BATestScriptListener>
(noOfDelayedNeeds));
this.secondPhasetestScriptListeners = Collections.synchronizedList(new ArrayList<BATestScriptListener>
(noOfNeeds-1));
// this.secondPhasetestScriptWithDelayListeners = new ArrayList<BATestScriptListener>(noOfDelayedNeeds);
}
protected abstract FacetType getParticipantFacetType();
protected abstract FacetType getCoordinatorFacetType();
@Override
protected void initializeEventListeners() {
final EventListenerContext ctx = getEventListenerContext();
final AdditionalParticipantCoordinatorBotContextWrapper botContextWrapper = (AdditionalParticipantCoordinatorBotContextWrapper) getBotContextWrapper();
final EventBus bus = getEventBus();
logger.info("info1: No of needs: "+noOfNeeds);
//wait for all needs to be created
WaitForNEventsListener allNeedsCreatedListener = new WaitForNEventsListener(ctx,
"waitForAllNeedsCreated",noOfNeeds);
bus.subscribe(NeedCreatedEvent.class, allNeedsCreatedListener);
//create needs every trigger execution until noOfNeeds are created
this.participantNeedCreator = new ActionOnEventListener(
ctx, "participantCreator",
new CreateNeedWithFacetsAction(ctx, botContextWrapper.getParticipantListName(), getParticipantFacetType().getURI()),
noOfNonDelayedNeeds - 1
);
bus.subscribe(ActEvent.class, this.participantNeedCreator);
//create needs every trigger execution until noOfNeeds are created
this.delayedParticipantNeedCreator = new ActionOnEventListener(
ctx, "delayedParticipantCreator",
new CreateNeedWithFacetsAction(ctx, botContextWrapper.getParticipantDelayedListName(), getParticipantFacetType().getURI()),
noOfDelayedNeeds
);
bus.subscribe(ActEvent.class, this.delayedParticipantNeedCreator);
//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);
final Iterator<BATestBotScript> firstPhasescriptIterator = firstPhaseScripts.iterator();
final Iterator<BATestBotScript> firstPhaseScriptWithDelayIterator = firstPhaseScriptsWithDelay.iterator();
final Iterator<BATestBotScript> secondPhasescriptIterator = secondPhaseScripts.iterator();
// final Iterator<BATestBotScript> secondPhasescriptWithDelayIterator = secondPhaseScripts.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 firstPhaseScriptWithDelayListenerFilter = new OrFilter();
final OrFilter secondPhaseScriptListenerFilter = new OrFilter();
// final OrFilter secondPhaseScriptWithDelayListenerFilter = 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) {
//create the listener that will execute the script actions
BATestScriptListener testScriptListener = new BATestScriptListener(ctx, firstPhasescriptIterator.next(), fromNeedURI,
toNeedURI, MILLIS_BETWEEN_MESSAGES);
//remember it so we can check its state later
firstPhasetestScriptListeners.add(testScriptListener);
//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,
secondPhasescriptIterator.next(),fromNeedURI,toNeedURI, MILLIS_BETWEEN_MESSAGES);
secondPhasetestScriptListeners.add(secondPhaseTestScriptListener);
secondPhaseScriptListenerFilter.addFilter(
new AcceptOnceFilter(
new FinishedEventFilter(secondPhaseTestScriptListener)));
}
};
ConnectFromListToListAction.ConnectHook scriptConnectWithDelayHook = new ConnectFromListToListAction.ConnectHook()
{
@Override
public void onConnect(final URI fromNeedURI, final URI toNeedURI) {
//create the listener that will execute the script actions
BATestScriptListener testScriptListener = new BATestScriptListener(ctx, firstPhaseScriptWithDelayIterator.next(),
fromNeedURI, toNeedURI, MILLIS_BETWEEN_MESSAGES);
//remember it so we can check its state later
firstPhasetestScriptWithDelayListeners.add(testScriptListener);
//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.
firstPhaseScriptWithDelayListenerFilter.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,
secondPhasescriptIterator.next(),fromNeedURI,toNeedURI, MILLIS_BETWEEN_MESSAGES);
secondPhasetestScriptListeners.add(secondPhaseTestScriptListener);
secondPhaseScriptListenerFilter.addFilter(
new AcceptOnceFilter(
new FinishedEventFilter(secondPhaseTestScriptListener)));
}
};
//when done, connect the participants to the coordinator
this.needConnector = new ActionOnEventListener(
ctx, "needConnector", new FinishedEventFilter(allNeedsCreatedListener),
new ConnectFromListToListAction(ctx, botContextWrapper.getCoordinatorListName(), botContextWrapper.getParticipantListName(),
getCoordinatorFacetType().getURI(),
getParticipantFacetType().getURI(), MILLIS_BETWEEN_MESSAGES,
scriptConnectHook, "Hi!"),1);
bus.subscribe(FinishedEvent.class, this.needConnector);
//wait until the non-delayed participants are connected and done with their scripts
BaseEventListener waitForNonDelayedConnectsListener = new WaitForNEventsListener(ctx,
firstPhaseScriptListenerFilter, noOfNonDelayedNeeds - 1);
bus.subscribe(FinishedEvent.class, waitForNonDelayedConnectsListener);
FinishedEventFilter allNonDelayedConnectedFilter = new FinishedEventFilter(waitForNonDelayedConnectsListener);
this.needConnectorWithDelay = new ActionOnEventListener(
ctx, "needConnectorWithDelay", allNonDelayedConnectedFilter,
new ConnectFromListToListAction(ctx, botContextWrapper.getCoordinatorListName(), botContextWrapper.getParticipantDelayedListName(),
getCoordinatorFacetType().getURI(),
getParticipantFacetType().getURI(), MILLIS_BETWEEN_MESSAGES,
scriptConnectWithDelayHook, "Hi!"),1);
bus.subscribe(FinishedEvent.class, this.needConnectorWithDelay); //TODO: MAKE THIS SO URI_LIST_NAME_PARTICIPANT_DELAYED "delayedParticipants" works again
//for each group member, there are 2 listeners waiting for messages. when they are all finished, we're done.
this.firstPhaseWithDelayDoneListener = new ActionOnceAfterNEventsListener(
ctx, "firstPhaseDoneWithDelayListener", firstPhaseScriptWithDelayListenerFilter,
noOfDelayedNeeds,
new BaseEventBotAction(ctx)
{
@Override
protected void doRun(final Event event, EventListener executingListener) throws Exception {
logger.debug("starting second phase");
logger.debug("non-delayed listeners: {}", firstPhasetestScriptListeners.size());
logger.debug("delayed listeners: {}", firstPhasetestScriptWithDelayListeners.size());
Iterator<BATestScriptListener> firstPhaseListeners = firstPhasetestScriptListeners.iterator();
Iterator<BATestScriptListener> firstPhaseWithDelayListeners = firstPhasetestScriptWithDelayListeners
.iterator();
Iterator<BATestScriptListener> combinedFirstPhaseListeners = Iterators.concat(firstPhaseListeners,
firstPhaseWithDelayListeners);
logger.debug("# of listeners in second phase: {}", secondPhasetestScriptListeners.size());
for(BATestScriptListener listener: secondPhasetestScriptListeners){
logger.debug("subscribing second phase listener {}", listener);
//subscribe it to the relevant events.
bus.subscribe(MessageFromOtherNeedEvent.class, listener);
bus.subscribe(SecondPhaseStartedEvent.class, listener);
BATestScriptListener correspondingFirstPhaseListener = combinedFirstPhaseListeners.next();
listener.setCoordinatorSideConnectionURI(correspondingFirstPhaseListener.getCoordinatorSideConnectionURI());
listener.setParticipantSideConnectionURI(correspondingFirstPhaseListener.getParticipantSideConnectionURI());
listener.updateFilterForBothConnectionURIs();
bus.publish(new SecondPhaseStartedEvent(
correspondingFirstPhaseListener.getCoordinatorURI(),
correspondingFirstPhaseListener.getCoordinatorSideConnectionURI(),
correspondingFirstPhaseListener.getParticipantURI()));
}
}
});
bus.subscribe(FinishedEvent.class, this.firstPhaseWithDelayDoneListener);
//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();
protected abstract List<BATestBotScript> getFirstPhaseScriptsWithDelay();
}