/*
* Copyright 2012 Research Studios Austria Forschungsges.m.b.H.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package won.bot.impl;
import won.bot.framework.bot.base.EventBot;
import won.bot.framework.bot.context.GroupBotContextWrapper;
import won.bot.framework.eventbot.action.impl.lifecycle.SignalWorkDoneAction;
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.wonmessage.ConnectFromListToListAction;
import won.bot.framework.eventbot.action.impl.wonmessage.OpenConnectionAction;
import won.bot.framework.eventbot.action.impl.wonmessage.RespondToMessageAction;
import won.bot.framework.eventbot.event.Event;
import won.bot.framework.eventbot.bus.EventBus;
import won.bot.framework.eventbot.EventListenerContext;
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.filter.impl.*;
import won.bot.framework.eventbot.listener.*;
import won.bot.framework.eventbot.action.*;
import won.bot.framework.eventbot.listener.impl.ActionOnEventListener;
import won.bot.framework.eventbot.listener.impl.ActionOnceAfterNEventsListener;
import won.bot.framework.eventbot.listener.impl.AutomaticMessageResponderListener;
import won.bot.framework.eventbot.listener.impl.WaitForNEventsListener;
import won.protocol.model.FacetType;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class GroupingBot extends EventBot
{
protected static final int NO_OF_GROUPMEMBERS = 5;
protected static final int NO_OF_MESSAGES = 5;
protected static final long MILLIS_BETWEEN_MESSAGES = 1;
//we use protected members so we can extend the class and
//access the listeners for unit test assertions and stats
//
//we use BaseEventListener as their types so we can access the generic
//functionality offered by that class
protected BaseEventListener groupMemberCreator;
protected BaseEventListener groupCreator;
protected BaseEventListener needConnector;
protected BaseEventListener autoOpener;
protected BaseEventListener autoResponderCreator;
protected BaseEventListener receiverFinishedListener;
protected BaseEventListener messagesDoneListener;
protected BaseEventListener conversationStarter;
protected BaseEventListener workDoneSignaller;
protected List<BaseEventListener> autoResponders;
protected List<BaseEventListener> messageCounters;
@Override
protected void initializeEventListeners() {
final EventListenerContext ctx = getEventListenerContext();
GroupBotContextWrapper botContextWrapper = (GroupBotContextWrapper) getBotContextWrapper();
EventBus bus = getEventBus();
//create needs every trigger execution until N needs are created
this.groupMemberCreator = new ActionOnEventListener(
ctx, "groupMemberCreator",
new CreateNeedWithFacetsAction(ctx, botContextWrapper.getNeedCreateListName()),
NO_OF_GROUPMEMBERS
);
bus.subscribe(ActEvent.class, this.groupMemberCreator);
//for each created need (in the group), add a listener that will auto-respond to messages directed at that need
//create a filter that only accepts events for needs in the group:
NeedUriInNamedListFilter groupMemberFilter = new NeedUriInNamedListFilter(ctx, botContextWrapper.getGroupMembersListName());
//remember the auto-responders in a list
this.autoResponders = new ArrayList<BaseEventListener>();
//remember the listeners that wait for all messages
this.messageCounters = new ArrayList<BaseEventListener>();
//make a composite filter, with one filter for each autoResponder that wait for the FinishedEvents the responders emit.
//that filter will be used to shut down all needs after all the autoResponders have finished.
final OrFilter mainAutoResponderFilter = new OrFilter();
//listen to NeedCreatedEvents
this.autoResponderCreator = new ActionOnEventListener(ctx, groupMemberFilter, new BaseEventBotAction(ctx)
{
@Override
protected void doRun(final Event event, EventListener executingListener) throws Exception {
//create a listener that automatically answers messages, only for that need URI
AutomaticMessageResponderListener listener = new AutomaticMessageResponderListener(ctx, "autoResponder",
NeedUriEventFilter.forEvent(event),
NO_OF_MESSAGES,
MILLIS_BETWEEN_MESSAGES);
//remember the listener for later
autoResponders.add(listener);
//add a filter that will wait for the FinishedEvent emitted by that listener
//wrap it in an acceptonce filter to make extra sure we count each listener only once.
mainAutoResponderFilter.addFilter(
new AcceptOnceFilter(
new FinishedEventFilter(listener)));
//create a listener that publishes a FinishedEvent after having received all messages from the group
WaitForNEventsListener waitForMessagesListener = new WaitForNEventsListener(ctx, "messageCounter",
NeedUriEventFilter.forEvent(event),
NO_OF_MESSAGES * (NO_OF_GROUPMEMBERS - 1));
messageCounters.add(waitForMessagesListener);
//add a filter that will wait for the FinishedEvent emitted by that listener
//wrap it in an acceptonce filter to make extra sure we count each listener only once.
mainAutoResponderFilter.addFilter(
new AcceptOnceFilter(
new FinishedEventFilter(waitForMessagesListener)));
//finally, subscribe to the message events
getEventBus().subscribe(MessageFromOtherNeedEvent.class, waitForMessagesListener);
getEventBus().subscribe(MessageFromOtherNeedEvent.class, listener);
}
});
getEventBus().subscribe(NeedCreatedEvent.class, this.autoResponderCreator);
//count until N needs were created, then create need with group facet (the others will connect to that facet)
this.groupCreator = new ActionOnceAfterNEventsListener(
ctx, "groupCreator",
NO_OF_GROUPMEMBERS,
new CreateNeedWithFacetsAction(ctx, botContextWrapper.getGroupMembersListName(), FacetType.GroupFacet.getURI()));
bus.subscribe(NeedCreatedEvent.class, this.groupCreator);
//wait for N+1 needCreatedEvents, then connect the members with the group facet of the third need
this.needConnector = new ActionOnceAfterNEventsListener(ctx, "needConnector", NO_OF_GROUPMEMBERS + 1,
new ConnectFromListToListAction(ctx, botContextWrapper.getGroupMembersListName(),
botContextWrapper.getGroupListName(),
FacetType.OwnerFacet
.getURI(),
FacetType.GroupFacet
.getURI(),
MILLIS_BETWEEN_MESSAGES,
"Hi from the " +
"GroupingBot!"));
bus.subscribe(NeedCreatedEvent.class, this.needConnector);
//add a listener that is informed of the connect/open events and that auto-opens
//subscribe it to:
// * connect events - so it responds with open
// * open events - so it responds with open (if the open received was the first open, and we still need to accept the connection)
this.autoOpener = new ActionOnEventListener(ctx, new OpenConnectionAction(ctx, "Hi from the GroupingBot!"));
bus.subscribe(ConnectFromOtherNeedEvent.class, this.autoOpener);
//now, once all connections have been opened, make 1 bot send a message to the group, the subsequent listener will cause let wild chatting to begin
this.conversationStarter = new ActionOnceAfterNEventsListener(ctx, "conversationStarter", NO_OF_GROUPMEMBERS,
new RespondToMessageAction(ctx,
MILLIS_BETWEEN_MESSAGES)
);
bus.subscribe(ConnectFromOtherNeedEvent.class, this.conversationStarter);
//for each group member, there are 2 listeners waiting for messages. when they are all finished, we're done.
this.messagesDoneListener = new ActionOnceAfterNEventsListener(
ctx, "messagesDoneListener", mainAutoResponderFilter,
NO_OF_GROUPMEMBERS * 2,
new DeactivateAllNeedsOfListAction(ctx, botContextWrapper.getGroupListName()));
bus.subscribe(FinishedEvent.class, this.messagesDoneListener);
//When the group facet need is deactivated, all connections are closed. wait for the close events and signal work done.
this.workDoneSignaller = new ActionOnceAfterNEventsListener(
ctx, "workDoneSignaller",
NO_OF_GROUPMEMBERS, new SignalWorkDoneAction(ctx)
);
bus.subscribe(CloseFromOtherNeedEvent.class, this.workDoneSignaller);
}
}