Skip to content

Commit

Permalink
Adding management of ParticipantLeft and ParticipantJoined, and utnin…
Browse files Browse the repository at this point in the history
…g sequencing system events

Closes: #72
Closes: #941 adding scope from ParticipantLeft/Joined,
MemberJoined/Left, and ContextJoined/Left. SpaceCreated/Destroyed are
not impacted beacause not sent by agents but spaces. AgentSpawned/Killed
was already scoped
  • Loading branch information
ngaud committed Oct 17, 2019
1 parent 137c935 commit 1ebc123
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 40 deletions.
85 changes: 85 additions & 0 deletions main/apiplugins/io.sarl.util/src/io/sarl/util/Events.sarl
@@ -0,0 +1,85 @@
/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2019 the original authors or authors.
*
* 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 io.sarl.util

import io.sarl.lang.core.Address
import io.sarl.lang.core.SpaceID
import java.util.UUID

/** Notifies other participants the member with agentID has effectively
* integrated the space.
* The event is fired within the default space of the context in which the agent entered a new space.
* The joining agent does not receive this event.
* The source of the event is the entering agent.
*/
final event ParticipantJoined {

/** The identifier of the space in which the agent is entering.
*/
val spaceID : SpaceID

/** The identifier of the agent that has entered the space.
*/
val agentID : UUID

/** Construct the event.
*
* @param source the address of the emitter.
* @param spaceID the identifier of the space.
* @param agentID the identifier of the agent.
*/
new (source : Address, spaceID : SpaceID, agentID : UUID) {
setSource(source)
this.spaceID = spaceID
this.agentID = agentID
}

}

/** Notifies other participants the member with agentID has effectively
* left the space.
* The event is fired within the default space of the context in which the agent left the space.
* The leaving agent does not receive this event.
* The source of the event is the leaving agent.
*/
final event ParticipantLeft {

/** The identifier of the space the agent just left.
*/
val spaceID : SpaceID

/** The identifier of the agent that has left the space.
*/
val agentID : UUID

/** Construct the event.
*
* @param source the address of the emitter.
* @param spaceID the identifier of the space.
* @param agentID the identifier of the agent.
*/
new (source : Address, spaceID : SpaceID, agentID : UUID) {
setSource(source)
this.spaceID = spaceID
this.agentID = agentID
}

}
Expand Up @@ -25,8 +25,6 @@ import io.sarl.lang.core.Address
import io.sarl.lang.core.EventListener
import io.sarl.lang.core.EventSpace



/**
* Event driven interaction space where agent are free register and unregister themselves.
* Agents should only register once in this type of space.
Expand All @@ -47,8 +45,9 @@ interface OpenEventSpace extends EventSpace {
*
* @param entity the entity to register.
* @return the entity's address in this space
* @fires ParticipantJoined in its enclosing Context default space.
*/
def register(entity : EventListener) : Address
def register(entity : EventListener) : Address fires ParticipantJoined

/**
* Unregisters the entity inside this space.
Expand All @@ -57,8 +56,9 @@ interface OpenEventSpace extends EventSpace {
*
* @param entity the entity to unregister.
* @return the former entity's address
* @fires ParticipantJoined in its enclosing Context default space.
*/
def unregister(entity : EventListener) : Address
def unregister(entity : EventListener) : Address fires ParticipantLeft

}

Expand Up @@ -74,25 +74,30 @@ public class ExternalContextAccessSkill extends BuiltinSkill implements External

private ClearableReference<Skill> skillBufferBehaviors;

/** Constructor.
/**
* Constructor.
*
* @param agent owner of the skill.
*/
ExternalContextAccessSkill(Agent agent) {
super(agent);
}

/** Replies the InternalEventBusCapacity skill as fast as possible.
/**
* Replies the InternalEventBusCapacity skill as fast as possible.
*
* @return the skill
*/
protected final InternalEventBusCapacity getInternalEventBusCapacitySkill() {
if (this.skillBufferInternalEventBusCapacity == null || this.skillBufferInternalEventBusCapacity.get() == null) {
if (this.skillBufferInternalEventBusCapacity == null
|| this.skillBufferInternalEventBusCapacity.get() == null) {
this.skillBufferInternalEventBusCapacity = $getSkill(InternalEventBusCapacity.class);
}
return $castSkill(InternalEventBusCapacity.class, this.skillBufferInternalEventBusCapacity);
}

/** Replies the Behaviors skill as fast as possible.
/**
* Replies the Behaviors skill as fast as possible.
*
* @return the skill
*/
Expand All @@ -103,7 +108,8 @@ protected final Behaviors getBehaviorsSkill() {
return $castSkill(Behaviors.class, this.skillBufferBehaviors);
}

/** {@inheritDoc}
/**
* {@inheritDoc}
*
* @deprecated since 0.10
*/
Expand Down Expand Up @@ -181,38 +187,43 @@ public boolean join(UUID futureContext, UUID futureContextDefaultSpaceID) {
}

this.contexts.add(futureContext);
fireContextJoined(futureContext, futureContextDefaultSpaceID);
fireMemberJoined(ac);

((OpenEventSpace) ac.getDefaultSpace()).register(getInternalEventBusCapacitySkill().asEventListener());

fireContextJoined(futureContext, futureContextDefaultSpaceID);
fireMemberJoined(ac);
return true;
}

/**
* Fires an {@link ContextJoined} event into the Inner Context default space of the owner agent to notify behaviors/members
* that a new context has been joined.
* Fires an {@link ContextJoined} event into the Inner Context default space of
* the owner agent to notify behaviors/members that a new context has been
* joined.
*
* @param futureContext ID of the newly joined context
* @param futureContextDefaultSpaceID ID of the default space of the newly joined context
* @param futureContext ID of the newly joined context
* @param futureContextDefaultSpaceID ID of the default space of the newly
* joined context
*/
protected final void fireContextJoined(UUID futureContext, UUID futureContextDefaultSpaceID) {
getBehaviorsSkill().wake(new ContextJoined(futureContext, futureContextDefaultSpaceID));
getBehaviorsSkill().wake(new ContextJoined(futureContext, futureContextDefaultSpaceID),
it -> it.getUUID() != getOwner().getID());
}

/**
* Fires an {@link MemberJoined} event into the newly joined parent Context default space to notify other context's members
* that a new agent joined this context.
* Fires an {@link MemberJoined} event into the newly joined parent Context
* default space to notify other context's members that a new agent joined this
* context.
*
* @param newJoinedContext the newly joined context to notify its members
*/
protected final void fireMemberJoined(AgentContext newJoinedContext) {
final EventSpace defSpace = newJoinedContext.getDefaultSpace();
defSpace.emit(
// No need to give an event source because the event's source is explicitly set below.
null,
new MemberJoined(defSpace.getAddress(getOwner().getID()), newJoinedContext.getID(), getOwner().getID(),
getOwner().getClass().getName()));
// No need to give an event source because the event's source is explicitly set
// below.
null, new MemberJoined(new Address(defSpace.getSpaceID(), getOwner().getID()), newJoinedContext.getID(),
getOwner().getID(), getOwner().getClass().getName()),
it -> it.getUUID() != getOwner().getID());
}

@Override
Expand All @@ -226,38 +237,40 @@ public boolean leave(UUID contextID) {
if (!this.contexts.contains(contextID)) {
return false;
}

// To send this event the agent must still be inside the context and its default space
fireContextLeft(contextID);
fireMemberLeft(ac);

((OpenEventSpace) ac.getDefaultSpace()).unregister(getInternalEventBusCapacitySkill().asEventListener());

return this.contexts.remove(contextID);
final boolean b = this.contexts.remove(contextID);
fireMemberLeft(ac);
fireContextLeft(contextID);
return b;
}

/**
* Fires an {@link ContextLeft} event into the Inner Context Default space of the owner agent to notify behaviors/members that
* the specified context has been left.
* Fires an {@link ContextLeft} event into the Inner Context Default space of
* the owner agent to notify behaviors/members that the specified context has
* been left.
*
* @param contextID the ID of context that will be left
*/
protected final void fireContextLeft(UUID contextID) {
getBehaviorsSkill().wake(new ContextLeft(contextID));
getBehaviorsSkill().wake(new ContextLeft(contextID),
it -> it.getUUID() != getOwner().getID());
}

/**
* Fires an {@link MemberLeft} event into the default space of the Context that will be left to notify other context's members
* that an agent has left this context.
* Fires an {@link MemberLeft} event into the default space of the Context that
* will be left to notify other context's members that an agent has left this
* context.
*
* @param leftContext the context that will be left
*/
protected final void fireMemberLeft(AgentContext leftContext) {
final EventSpace defSpace = leftContext.getDefaultSpace();
defSpace.emit(
// No need to give an event source because the event's source is explicitly set below.
null,
new MemberLeft(defSpace.getAddress(getOwner().getID()), getOwner().getID(), getOwner().getClass().getName()));
// No need to give an event source because the event's source is explicitly set
// below.
null, new MemberLeft(new Address(defSpace.getSpaceID(), getOwner().getID()), getOwner().getID(),
getOwner().getClass().getName()),
it -> it.getUUID() != getOwner().getID());
}

@Override
Expand Down
Expand Up @@ -23,14 +23,19 @@

import java.util.concurrent.locks.ReadWriteLock;

import com.google.inject.Inject;
import com.google.inject.Provider;

import io.janusproject.services.contextspace.ContextSpaceService;
import io.janusproject.services.distributeddata.DistributedDataStructureService;

import io.sarl.lang.core.Address;
import io.sarl.lang.core.AgentContext;
import io.sarl.lang.core.EventListener;
import io.sarl.lang.core.EventSpace;
import io.sarl.lang.core.SpaceID;
import io.sarl.util.OpenEventSpace;
import io.sarl.util.ParticipantJoined;
import io.sarl.util.ParticipantLeft;

/**
* Default implementation of an event space.
Expand All @@ -44,6 +49,9 @@
*/
public class EventSpaceImpl extends AbstractEventSpace implements OpenEventSpace {

@Inject
private ContextSpaceService contextRepository;

/**
* Constructs an event space.
*
Expand All @@ -57,12 +65,49 @@ public EventSpaceImpl(SpaceID id, DistributedDataStructureService factory, Provi

@Override
public Address register(EventListener entity) {
return getParticipantInternalDataStructure().registerParticipant(new Address(getSpaceID(), entity.getID()), entity);
final Address a = getParticipantInternalDataStructure().registerParticipant(new Address(getSpaceID(), entity.getID()), entity);
fireParticipantJoined(a);
return a;
}

@Override
public Address unregister(EventListener entity) {
return getParticipantInternalDataStructure().unregisterParticipant(entity);
final Address a = getParticipantInternalDataStructure().unregisterParticipant(entity);
fireParticipantLeft(a);
return a;
}

/**
* Fires an {@link ParticipantJoined} event into the default space of the current Context to notify other context's members
* that a new agent joined this space.
* @param newAgentAddress - the address of the agent.
*/
protected final void fireParticipantJoined(Address newAgentAddress) {
final AgentContext enclosingContext = this.contextRepository.getContext(newAgentAddress.getSpaceID().getContextID());
final EventSpace defSpace = enclosingContext.getDefaultSpace();
defSpace.emit(
// No need to give an event source because the event's source is explicitly set below.
null,
new ParticipantJoined(defSpace.getAddress(newAgentAddress.getUUID()), newAgentAddress.getSpaceID(), newAgentAddress.getUUID()),
it -> it.getUUID() != newAgentAddress.getUUID());
}

/**
* Fires an {@link ParticipantLeft} event into the default space of the current Context to notify other context's members
* that an agent left this space.
* @param agentAddress - address of the agent leaving the space.
*/
protected final void fireParticipantLeft(Address agentAddress) {
final AgentContext enclosingContext = this.contextRepository.getContext(agentAddress.getSpaceID().getContextID());
final EventSpace defSpace = enclosingContext.getDefaultSpace();
//Since this agent may already have quit the default space at the moment this event is sent,
//it is mandatory to recreate an address to be sure the event has a source
defSpace.emit(
// No need to give an event source because the event's source is explicitly set below.
null,
new ParticipantLeft(new Address(defSpace.getSpaceID(), agentAddress.getUUID()),
agentAddress.getSpaceID(), agentAddress.getUUID()),
it -> it.getUUID() != agentAddress.getUUID());
}

}

0 comments on commit 1ebc123

Please sign in to comment.