/* * $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.bic; import java.text.MessageFormat; import java.util.Collections; import java.util.Set; import java.util.UUID; import com.google.common.collect.Sets; import com.google.inject.Inject; import io.janusproject.services.contextspace.ContextSpaceService; import io.sarl.core.Behaviors; import io.sarl.core.ContextJoined; import io.sarl.core.ContextLeft; import io.sarl.core.ExternalContextAccess; import io.sarl.core.MemberJoined; import io.sarl.core.MemberLeft; import io.sarl.lang.core.Agent; import io.sarl.lang.core.AgentContext; import io.sarl.lang.core.Event; import io.sarl.lang.core.EventSpace; import io.sarl.lang.core.Skill; import io.sarl.lang.core.Space; import io.sarl.lang.core.SpaceID; import io.sarl.lang.util.ClearableReference; import io.sarl.lang.util.SynchronizedCollection; import io.sarl.util.Collections3; import io.sarl.util.OpenEventSpace; /** * Skill that permits to access to the context in which the agent is located. * * @author $Author: srodriguez$ * @author $Author: ngaud$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public class ExternalContextAccessSkill extends BuiltinSkill implements ExternalContextAccess { private static int installationOrder = -1; private final Set<UUID> contexts = Sets.newConcurrentHashSet(); @Inject private ContextSpaceService contextRepository; private ClearableReference<Skill> skillBufferInternalEventBusCapacity; private ClearableReference<Skill> skillBufferBehaviors; /** * @param agent - owner of the skill. */ ExternalContextAccessSkill(Agent agent) { super(agent); } /** Replies the InternalEventBusCapacity skill as fast as possible. * * @return the skill */ protected final InternalEventBusCapacity getInternalEventBusCapacitySkill() { 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. * * @return the skill */ protected final Behaviors getBehaviorsSkill() { if (this.skillBufferBehaviors == null || this.skillBufferBehaviors.get() == null) { this.skillBufferBehaviors = $getSkill(Behaviors.class); } return $castSkill(Behaviors.class, this.skillBufferBehaviors); } @Override public int getInstallationOrder() { if (installationOrder < 0) { installationOrder = installationOrder(this); } return installationOrder; } @Override protected String attributesToString() { return super.attributesToString() + ", contexts = " + this.contextRepository.toString(); //$NON-NLS-1$ } @Override protected void install() { final AgentContext ac = this.contextRepository.getContext(getOwner().getParentID()); join(ac.getID(), ac.getDefaultSpace().getSpaceID().getID()); } @Override protected void uninstall(UninstallationStage stage) { if (stage == UninstallationStage.POST_DESTROY_EVENT) { // Leave all contexts including the default one. for (final UUID contextID : this.contexts) { leave(contextID); } } } @Override public SynchronizedCollection<AgentContext> getAllContexts() { return Collections3.synchronizedCollection( Collections.unmodifiableCollection(this.contextRepository.getContexts(this.contexts)), this.contextRepository.mutex()); } @Override public AgentContext getContext(UUID contextID) { assert contextID != null; if (!this.contexts.contains(contextID)) { throw new IllegalArgumentException(MessageFormat.format(Messages.ExternalContextAccessSkill_0, contextID)); } return this.contextRepository.getContext(contextID); } @Override public void join(UUID futureContext, UUID futureContextDefaultSpaceID) { assert futureContext != null; assert futureContextDefaultSpaceID != null; if (this.contexts.contains(futureContext)) { return; } final AgentContext ac = this.contextRepository.getContext(futureContext); assert ac != null : "Unknown Context"; //$NON-NLS-1$ if (!futureContextDefaultSpaceID.equals(ac.getDefaultSpace().getSpaceID().getID())) { throw new IllegalArgumentException(MessageFormat.format(Messages.ExternalContextAccessSkill_1, futureContextDefaultSpaceID)); } this.contexts.add(futureContext); ((OpenEventSpace) ac.getDefaultSpace()).register(getInternalEventBusCapacitySkill().asEventListener()); fireContextJoined(futureContext, futureContextDefaultSpaceID); fireMemberJoined(ac); } /** * 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 */ protected final void fireContextJoined(UUID futureContext, UUID futureContextDefaultSpaceID) { getBehaviorsSkill().wake(new ContextJoined(futureContext, futureContextDefaultSpaceID)); } /** * 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(new MemberJoined(defSpace.getAddress(getOwner().getID()), newJoinedContext.getID(), getOwner().getID(), getOwner().getClass().getName())); } @Override public void leave(UUID contextID) { assert contextID != null; final AgentContext ac = this.contextRepository.getContext(contextID); assert ac != null : "Unknown Context"; //$NON-NLS-1$ if (!this.contexts.contains(contextID)) { return; } // 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()); this.contexts.remove(contextID); } /** * 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)); } /** * 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( new MemberLeft(defSpace.getAddress(getOwner().getID()), getOwner().getID(), getOwner().getClass().getName())); } @Override public boolean isInSpace(Event event, Space space) { return isInSpace(event, space.getSpaceID()); } @Override public boolean isInSpace(Event event, SpaceID spaceID) { return spaceID.equals(event.getSource().getSpaceId()); } @Override public boolean isInSpace(Event event, UUID spaceID) { return spaceID.equals(event.getSource().getSpaceId().getID()); } }