/* * $Id$ * * SARL is an general-purpose agent programming language. * More details on http://www.sarl.io * * Copyright (C) 2014-2017 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.janusproject.kernel.space; import java.util.UUID; import com.google.inject.Inject; import io.janusproject.kernel.repository.UniqueAddressParticipantRepository; import io.janusproject.services.distributeddata.DistributedDataStructureService; import io.janusproject.services.executor.ExecutorService; import io.janusproject.services.logging.LogService; import io.janusproject.services.network.NetworkService; import io.sarl.lang.core.Address; import io.sarl.lang.core.Event; import io.sarl.lang.core.EventListener; import io.sarl.lang.core.Scope; import io.sarl.lang.core.SpaceID; import io.sarl.lang.util.SynchronizedCollection; import io.sarl.lang.util.SynchronizedSet; import io.sarl.util.Collections3; import io.sarl.util.Scopes; /** * Abstract implementation of an event space. * * @author $Author: srodriguez$ * @author $Author: ngaud$ * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public abstract class AbstractEventSpace extends SpaceBase { /** * Logging service. */ @Inject protected LogService logger; /** * Executor service. */ @Inject protected ExecutorService executorService; /** * Network service. */ @Inject private NetworkService network; /** * List of participants in this space. */ private final UniqueAddressParticipantRepository<Address> participants; /** * Constructs an event space. * * @param id - identifier of the space. * @param factory - factory that is used to create the internal data structure. */ public AbstractEventSpace(SpaceID id, DistributedDataStructureService factory) { super(id); this.participants = new UniqueAddressParticipantRepository<>(getSpaceID().getID().toString() + "-participants", //$NON-NLS-1$ factory); } /** Replies the internal datastructure that stores the participants to this space. * * @return the internal data structure. */ protected UniqueAddressParticipantRepository<Address> getParticipantInternalDataStructure() { return this.participants; } /** * Replies the address associated to the given participant. * * @param entity - instance of a participant. * @return the address of the participant with the given id. */ public final Address getAddress(EventListener entity) { return getAddress(entity.getID()); } /** * Replies the address associated with the given id. * * @param id - the identifier of the participant. * @return the address. */ public Address getAddress(UUID id) { return getParticipantInternalDataStructure().getAddress(id); } /** * Emit the given event in the given scope. * * <p>This function emits on the internal event bus of the agent (call to {@link #doEmit(Event, Scope)}), and on the network. * * @param event - the event to emit. * @param scope - description of the scope of the event, i.e. the receivers of the event. */ public final void emit(Event event, Scope<Address> scope) { assert event != null; assert event.getSource() != null : "Every event must have a source"; //$NON-NLS-1$ assert this.getSpaceID().equals(event.getSource().getSpaceId()) : "The source address must belong to this space"; //$NON-NLS-1$ try { final Scope<Address> scopeInstance = (scope == null) ? Scopes.<Address>allParticipants() : scope; this.network.publish(scopeInstance, event); doEmit(event, scopeInstance); } catch (Throwable e) { this.logger.error(Messages.AbstractEventSpace_0, event, scope, e); } } /** * Do the emission of the event. * * <p>This function emits the event <strong>only on the internal event bus</strong> of the agents. * * @param event - the event to emit. * @param scope - description of the scope of the event, i.e. the receivers of the event. */ protected void doEmit(Event event, Scope<? super Address> scope) { assert scope != null; assert event != null; final UniqueAddressParticipantRepository<Address> particips = getParticipantInternalDataStructure(); final SynchronizedCollection<EventListener> listeners = particips.getListeners(); synchronized (listeners.mutex()) { for (final EventListener agent : listeners) { if (scope.matches(getAddress(agent))) { // TODO Verify the agent is still alive and running this.executorService.submit(new AsyncRunner(agent, event)); } } } } @Override public SynchronizedSet<UUID> getParticipants() { return Collections3.unmodifiableSynchronizedSet(getParticipantInternalDataStructure().getParticipantIDs()); } @Override public String toString() { return getSpaceID().toString(); } @SuppressWarnings("unchecked") @Override public void eventReceived(SpaceID space, Scope<?> scope, Event event) { try { AbstractEventSpace.this.doEmit(event, (Scope<Address>) scope); } catch (Exception e) { this.logger.error(Messages.AbstractEventSpace_1, e); } } /** * Asynchronous runner. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class AsyncRunner implements Runnable { private final EventListener agent; private final Event event; /** * Construct. * @param agent the agent listener. * @param event the event. */ AsyncRunner(EventListener agent, Event event) { this.agent = agent; this.event = event; } @Override public void run() { this.agent.receiveEvent(this.event); } @Override public String toString() { return "[agent=" + this.agent + "; event=" + this.event + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ } } }