/*********************************************************************/
/* */
/* AgentService Framework */
/* www.agentservice.it */
/* */
/* Copyright (C) 2007 l.i.d.o. - DIST, University of Genoa, Italy */
/* GNU Lesser General Public License */
/* */
/* This library is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Lesser General Public */
/* License as published by the Free Software Foundation, */
/* version 2.1 of the License. */
/* */
/* This library is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
/* Lesser General Public License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public */
/* License along with this library; if not, write to the */
/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */
/* Boston, MA 02111-1307, USA. */
/* */
/*********************************************************************/
using System;
using System.Threading;
using AgentService.Agent;
using AgentService.Agent.Knowledge;
using AgentService.Agent.Behaviour;
using AgentService.Agent.Runtime;
using AgentService.Platform.MTS;
using AgentService.Platform.Modules;
namespace AgentService.Platform.AMS
{
///
/// Class AMS. Specific AMS Agent class
///
public class AMS : AgentService.Agent.Agent, IAMS
{
///
/// Class DefaultWhitePagesServer. Default implemetation of the IWhitePagesServer
//// Interface provided by the AMS. Handles queries from WhitePages clients.
///
private class DefaultWhitePagesServer: IWhitePagesServer
{
///
/// reference to the AMS agent
///
private AMS ams;
///
/// Constructs a DefaultWhitePagesServer object, bound the given AMS agent
///
/// reference to the AMS agent
public DefaultWhitePagesServer(AMS ams)
{
this.ams = ams;
}
#region IWhitePagesServer Members
///
/// Searches for all the agents which names satisfy the given regex pattern
///
/// regular expression pattern that defines the selction criteria
/// collection of Agent Description objects
public AgentDescription[] Search(string pattern)
{
AgentDescriptionCollection descriptions = new AgentDescriptionCollection();
lock(this.ams.registeredAgents)
{
// TODO: create and compile a regex before using it
// it is faster!!!!
try
{
foreach(AgentDescription ad in this.ams.registeredAgents)
{
System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(ad.AID.Name,pattern);
if(match.Success)
{
descriptions.Add(ad);
}
}
}
catch(ArgumentException aex)
{
// TODO: log invalid pattern?
}
}
lock (this.ams.registeredMobileAgents)
{
// TODO: create and compile a regex before using it
// it is faster!!!!
try
{
foreach (AgentDescription ad in this.ams.registeredMobileAgents)
{
System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(ad.AID.Name, pattern);
if (match.Success)
{
descriptions.Add(ad);
}
}
}
catch (ArgumentException aex)
{
// TODO: log invalid pattern?
}
}
AgentDescription[] ads = new AgentDescription[descriptions.Count];
descriptions.CopyTo(ads,0);
return ads;
}
///
/// Searches for the agent that has the given name.
///
/// name of the agent to search for
/// Agent Description of the agent if found, null otherwise
public AgentDescription SearchExact(string name)
{
lock(this.ams.registeredAgents)
{
foreach(AgentDescription ad in this.ams.registeredAgents)
{
if(ad.AID.Name.Equals(name))
{
return ad;
}
}
}
lock(this.ams.registeredMobileAgents)
foreach (AgentDescription ad in this.ams.registeredMobileAgents)
{
if (ad.AID.Name.Equals(name))
{
return ad;
}
}
return null;
}
#endregion
}
# region Members
///
/// Reference to the hosting platform.
///
private AgentService.Platform.Platform platform;
///
/// Collection of agents managed by the AMS.
///
internal AgentCollection agents;
internal AIDCollection mobileAgents;
///
/// Active Agents list.
///
private AIDCollection activeAgents;
///
/// Collection of the AID of the registered agents.
///
private AgentDescriptionCollection registeredAgents;
///
/// Gets a copy of the collection of agents registered with the ams
///
public AgentDescriptionCollection RegisteredAgents
{
get
{
lock(this.registeredAgents)
{
AgentDescriptionCollection adc = new AgentDescriptionCollection(this.registeredAgents.Count);
adc.Add(this.registeredAgents);
return adc;
}
}
}
///
/// Collection of the AID of the registered mobile agents.
///
private AgentDescriptionCollection registeredMobileAgents;
///
/// Gets a copy of the collection of mobile agents registered with the ams
///
public AgentDescriptionCollection RegisteredMobileAgents
{
get
{
lock (this.registeredMobileAgents)
{
AgentDescriptionCollection adc = new AgentDescriptionCollection(this.registeredMobileAgents.Count);
adc.Add(this.registeredMobileAgents);
return adc;
}
}
}
///
/// Suspended Agents list.
///
private AIDCollection suspendedAgents;
///
/// Waiting Agents list.
///
private AIDCollection waitingAgents;
///
/// White Pages Services implementing the IWhitePages interface
///
private IWhitePagesServer whitePages;
///
/// Gets the White Pages service.
///
public IWhitePagesServer WhitePages
{
get
{
return this.whitePages;
}
}
///
/// Local queue of messages.
///
private AgentMessageCollection messageQueue;
///
/// Flag that indicates if the moveAgent thread is active or not
///
private bool isMoveAgentThreadActive;
///
/// A collection representing all pending requests of move agents
///
private System.Collections.Queue pendingMoveRequests;
///
/// It's an id of the agent that is received. If it's value is -1
/// it means that no agent is being transferred
///
private System.Collections.ArrayList receivedTickets;
///
/// Thred that controls the transfer of agents when the AMS is acting
/// as client
///
private System.Threading.Thread moveAgentThread;
///
/// Source of the interrupt for the moveAgentThreaf:
///
private SourceOfInterrupt sourceOfInterrupt;
///
/// List of agents in the transit state
///
private AIDCollection transitAgents;
///
/// Thread that check for messages the local message queue
///
private System.Threading.Thread checkMessageQueue;
///
/// ManualReset event used to respond to asynchronous messages
/// that have to be checked.
///
internal System.Threading.ManualResetEvent checkMessagesInterruptEvent;
///
/// ManualReset event used to handle the migration process
///
private System.Threading.ManualResetEvent moveAgentInterruptEvent;
///
/// flag that indicates if the CheckMessage thread has to be terminated
///
internal bool bTerminateCMThread;
///
/// current message that needs to be processed.
///
private AgentMessage currentMessage;
///
/// property token that is used to change
/// the state of agent description instances.
///
private int token;
# endregion
# region Constructor
///
/// Construcs an AMS agent related to the given platform.
///
/// The Platform's description
public AMS(IPlatform platform)
{
this.platform = (Platform) platform;
this.aid = new AID(this.platform.amsName, this.platform.Description.Name);
this.agents = new AgentCollection();
this.registeredAgents = new AgentDescriptionCollection();
this.registeredMobileAgents = new AgentDescriptionCollection();
this.activeAgents = new AIDCollection();
this.mobileAgents = new AIDCollection();
this.suspendedAgents = new AIDCollection();
this.waitingAgents = new AIDCollection();
this.whitePages = new DefaultWhitePagesServer(this);
// added
this.messageQueue = new AgentMessageCollection();
this.receivedTickets = new System.Collections.ArrayList();
this.transitAgents = new AIDCollection();
this.currentMessage = new AgentMessage();
this.token = new System.Random().Next();
}
# endregion
# region Manage Agent SetUp and Registration
///
/// Initites and register the created agent with the AMS.
///
/// Agent.
/// the configfile of the Agent
/// List of value to inizialize Agent's attribute
/// persistence flag
/// domain flag
///
public void Register(AgentService.Agent.IAgent agent, string configFile, bool bPersist)
// public void Register(AgentService.Agent.Agent agent, object configFile, bool bPersist)
{
lock(this.registeredAgents)
{
// if the agent has not been registered
if (this.registeredAgents.Contains(agent.AID) == false)
{
AgentDescription ad = new AgentDescription(agent.AID,"",AgentState.Initiated, bPersist, this.token);
this.registeredAgents.Add(ad);
this.agents.Add(agent);
// initialize method and state moved outside the register method
// agent.Initialize(configFile);
// agent.State = AgentState.Initiated;
if (bPersist)
{
bool bOk = this.platform.Persistence.PersistAgent(agent.GetPersistent());
if (bOk == false)
{
// ERROR !!!
}
}
this.updateKnowledge();
}
else
{
AgentService.Generic.AgentServiceException agsex = new AgentService.Generic.AgentServiceException("Cannot register twice the same agent");
throw agsex;
}
}
}
///
/// Initites and register the created agent with the AMS.
///
/// the aid of the mobile agents
/// persistence flag
public bool RegisterMobile(AID aid)
{
bool bOk;
lock (this.registeredAgents)
{
if (this.registeredAgents.Contains(aid) == false) bOk = true;
else bOk = false;
}
if (bOk)
{
lock (this.registeredMobileAgents)
{
// if the agent has not been registered
if (this.registeredMobileAgents.Contains(aid) == false)
{
AgentDescription ad = new AgentDescription(aid, "", AgentState.Initiated, false, this.token);
this.registeredMobileAgents.Add(ad);
this.mobileAgents.Add(aid);
bOk = true;
}
else
{
bOk = false;
AgentService.Generic.AgentServiceException agsex = new AgentService.Generic.AgentServiceException("Cannot register twice the same agent");
throw agsex;
}
}
}
return bOk;
}
///
/// Starts agent activity
///
/// agent identifier to be started
public void Activate(AID aid)
{
// lock added
lock (this.agents)
{
int ndx = this.agents.IndexOf(aid);
if(ndx > -1)
{
Agent.IAgent agent = this.agents[ndx];
// schedules the agent (AMS.Schedule)
this.activeAgents.Add(aid);
agent.State = AgentState.Active;
agent.Activate();
// end - schedule
}
}
}
///
/// Starts the activity of all the registered agents
///
public void ActivateAll()
{
lock(this.agents)
{
foreach(AgentService.Agent.IAgent agent in this.agents)
{
if(agent.State == AgentState.Initiated)
{
this.activeAgents.Add(agent.AID);
agent.State = AgentState.Active;
agent.Activate();
}
}
}
}
///
/// Puts the agent in the previous persisted state.
///
/// Agent.
public void Resume(AgentService.Agent.IAgent agent)
{
// lock and bFound added
bool bFound = false;
lock(this.registeredAgents)
{
if (this.registeredAgents.Contains(agent.AID) == true)
{
bFound = true;
}
}
if (bFound == true)
{
lock (this.agents)
{
// CHANGE 6/2/2006 - check if the agent is alredy in
// the agents collection
if (this.agents.Contains(agent) == false)
{
this.agents.Add(agent);
}
switch(agent.State)
{
case AgentState.Active:
agent.Activate();
this.activeAgents.Add(agent.AID);
break;
case(AgentState.Initiated):
agent.State = AgentState.Active;
agent.Activate();
this.activeAgents.Add(agent.AID);
break;
case AgentState.Suspended:
this.suspendedAgents.Add(agent.AID);
break;
case AgentState.Waiting:
this.waitingAgents.Add(agent.AID);
break;
case AgentState.Transit:
// CHANGE 6/2/2006 - added support to the transit state
this.transitAgents.Add(agent.AID);
break;
case AgentState.Unknown:
//Log not supported exception
break;
}
}
}
}
///
/// Deregister a specific Agent. Deregistration
/// implies the removal of the agent object from
/// the collection of managed agents
///
/// The Agent's AID
public bool Deregister(AID aid, bool bDestroy)
{
bool bPersistent = false;
lock(this.registeredAgents)
{
int ndx = this.registeredAgents.IndexOf(aid);
if (ndx > -1)
{
int agent_ndx = this.agents.IndexOf(aid);
if (agent_ndx > -1)
{
try
{
AgentService.Agent.IAgent agent = this.agents[agent_ndx];
bPersistent = agent.IsPersistent;
switch(agent.State)
{
case AgentState.Active:
this.activeAgents.Remove(aid);
break;
case AgentState.Suspended:
this.suspendedAgents.Remove(aid);
break;
case AgentState.Waiting:
this.waitingAgents.Remove(aid);
break;
// CHANGE: 6/2/2006 - added support to the transit state
case AgentState.Transit:
this.transitAgents.Remove(aid);
break;
}
//MOD: Marco Garr: added lock on agents
// to resolve concurrency with
// ActivateAll method
lock (this.agents)
{
this.agents.Remove(agent);
}
agent.State = AgentState.Unknown;
if (bDestroy == true)
{
this.Destroy(aid);
}
}
catch (IndexOutOfRangeException ex)
{
// TODO: log exception
// some internal error
// log the exception
}
}
this.registeredAgents.Remove(aid);
if (bPersistent == true)
{
bool bOk = this.platform.Persistence.RemoveAgent(aid);
if (bOk == false)
{
// TODO: ERROR !!!
}
}
this.updateKnowledge();
}
return (ndx > -1);
}
}
///
/// Deregister a specific Agent. Deregistration
/// implies the removal of the agent object from
/// the collection of managed agents
///
/// The Agent's AID
public bool DeregisterMobile(AID aid)
{
bool bPersistent = false;
lock (this.registeredMobileAgents)
{
int ndx = this.registeredMobileAgents.IndexOf(aid);
if (ndx > -1)
{
int agent_ndx = this.mobileAgents.IndexOf(aid);
if (agent_ndx > -1)
{
try
{
lock (this.mobileAgents)
{
this.mobileAgents.Remove(aid);
}
}
catch (IndexOutOfRangeException ex)
{
// TODO: log exception
// some internal error
// log the exception
}
}
this.registeredMobileAgents.Remove(aid);
}
return (ndx > -1);
}
}
# endregion
#region Threads
///
/// Communicate with the remote AMS to negotiate the transfer of an agent.
/// If the negotiate is succesfull, it delegates the transferring operation
/// to the mobility module
///
private void MoveAgent()
{
this.isMoveAgentThreadActive = true;
if (this.moveAgentInterruptEvent == null)
{
this.moveAgentInterruptEvent = new ManualResetEvent(false);
}
else
{
this.moveAgentInterruptEvent.Reset();
}
TimeSpan timeout;
int count;
int ticket;
MoveRequest activeRequest;
System.Random random = new Random(35);
lock (this.pendingMoveRequests)
{
count = this.pendingMoveRequests.Count;
}
// if count > 0 there are moverequests to satisfy
while(count > 0)
{
lock (this.pendingMoveRequests)
{
activeRequest = (MoveRequest)this.pendingMoveRequests.Dequeue();
}
// get the timeout from the transferring request
timeout = activeRequest.Timeout;
ticket = random.Next();
// step 1.1: send to the remote AMS the request to transfer an agent
AID destinationAID = new AID(AID.AMS,activeRequest.DestinationPlatform);
MessageBody body = new MessageBody();
body.Add("transferMessage",TransferMessage.Request);
body.Add("timeout",activeRequest.Timeout);
body.Add("ticket",ticket);
AgentMessage msg = new AgentMessage("",body,destinationAID);
msg.Envelope.Date = System.DateTime.Now;
msg.Envelope.From = new AID(AID.AMS,this.platform.Configuration.PlatformDescription.Name);
this.platform.Messaging.SendMessage(msg);
// step 1.2: get response
bool transferOk;
TimeSpan receivedTimeout;
// get the start time to update timeout
long timeBeforeSleep = System.DateTime.Now.Ticks;
// wait for the incoming message, and check if it is the correct one
this.moveAgentInterruptEvent.WaitOne(timeout,false);
this.moveAgentInterruptEvent.Reset();
// get time after sleep, calculate the amount of time
//elapsed and update the timeout value
long timeAfterSleep = System.DateTime.Now.Ticks;
long sleepTime = timeAfterSleep - timeBeforeSleep;
timeout = timeout.Subtract(new TimeSpan(sleepTime));
AgentService.Platform.MTS.AgentMessage recMsg = null;
if (this.sourceOfInterrupt == SourceOfInterrupt.ResponseOfTransferRequest)
{
this.sourceOfInterrupt = SourceOfInterrupt.Default;
lock (this.currentMessage)
{
recMsg = this.currentMessage;
this.currentMessage = null;
}
}
// timeout exceed or error in the transfer process
else
{
this.AbortMoveOperation(ticket,destinationAID,activeRequest.AidOfAgentToMove);
this.RestartAgentNotTransferred(activeRequest);
lock(this.pendingMoveRequests)
{
count = this.pendingMoveRequests.Count;
}
continue;
}
Console.WriteLine("Prosecuzione..");
if ((recMsg.Envelope.From.HapName != activeRequest.DestinationPlatform) &&
((int)recMsg.Body["ticket"] != ticket))
{
this.AbortMoveOperation(ticket,destinationAID,activeRequest.AidOfAgentToMove);
this.RestartAgentNotTransferred(activeRequest);
lock(this.pendingMoveRequests)
{
count = this.pendingMoveRequests.Count;
}
continue;
}
transferOk = (bool)recMsg.Body["value"];
receivedTimeout = (TimeSpan)recMsg.Body["timeout"];
//step 2.1: if response is positive, persist the agent
if (transferOk == true)
{
// refresh timeout value if the timeout received from the
// remote AMS is smaller
if (receivedTimeout.CompareTo(timeout) == -1)
{
timeout = receivedTimeout;
}
AgentService.Agent.AgentPersistent persistedAgent = null;
AgentService.Agent.AgentProfile profile = null;
lock (this.transitAgents)
{
int ndx = this.transitAgents.IndexOf(activeRequest.AidOfAgentToMove);
if (ndx > -1)
{
ndx = this.agents.IndexOf(activeRequest.AidOfAgentToMove);
if (ndx > -1)
{
AgentService.Agent.IAgent agent = this.agents[ndx];
persistedAgent = agent.GetPersistent();
profile = agent.Profile;
}
else
{
// error
}
}
else
{
// the agent is not in transit state
}
}
// step 3: assign to the mobility module the task to transfer
// the persisted agent
if (persistedAgent != null)
{
// get agentProfile and set the ticket
profile.Ticket = ticket;
// get the mobility module and move agent
IPlatformModule[] modules = this.platform.Modules;
foreach (IPlatformModule module in modules)
{
Type type = module.GetType().GetInterface(typeof(AgentService.Platform.Modules.IMobilityModule).FullName);
if (type != null)
{
// here i use services from the mobility module.
// I also update the timeout value, considering the time
// spent to transfer the agent
long timeBeforeTransfer = System.DateTime.Now.Ticks;
bool isAgentTransferred = ((IMobilityModule)module).MoveAgent(profile,persistedAgent,activeRequest.DestinationPlatform);
if (isAgentTransferred == true)
{
long timeAfterTransfer = System.DateTime.Now.Ticks;
long transferTime = timeAfterTransfer - timeBeforeTransfer;
timeout = timeout.Subtract(new TimeSpan(transferTime));
if (timeout.Ticks < 0)
{
// TODO: too much time is elapsed. Take proper actions
}
bool transferSuccesfull = false;
bool transferFailed = false;
this.moveAgentInterruptEvent.WaitOne(timeout,false);
// if Source of interrpt is default, it means that the
// timeout is exceeded. Both the transferSuccesfull and
// the transferFailed variables are set to false
if (this.sourceOfInterrupt == SourceOfInterrupt.TransferSuccesfull)
{
transferSuccesfull = true;
}
else if (this.sourceOfInterrupt == SourceOfInterrupt.TransferFailed)
{
transferFailed = true;
}
this.sourceOfInterrupt = SourceOfInterrupt.Default;
if (transferSuccesfull == true)
{
// send an ack to the remote AMS, containing the AID
// of the transferred agent
MessageBody endBody = new MessageBody();
endBody.Add("transferMessage",TransferMessage.SuccesfullAck);
endBody.Add("aid",activeRequest.AidOfAgentToMove);
AgentMessage endMsg = new AgentMessage("",endBody,destinationAID);
endMsg.Envelope.Date = System.DateTime.Now;
endMsg.Envelope.From = new AID(AID.AMS,this.platform.Configuration.PlatformDescription.Name);
this.platform.Messaging.SendMessage(endMsg);
// stop the agent, kill it and Remove all agent's structures
lock (this.transitAgents)
{
int ndx = this.transitAgents.IndexOf(activeRequest.AidOfAgentToMove);
if (ndx > -1)
{
this.transitAgents.Remove(activeRequest.AidOfAgentToMove);
ndx = this.agents.IndexOf(activeRequest.AidOfAgentToMove);
if (ndx > -1)
{
Agent.IAgent agent = this.agents[ndx];
agent.CommitMove(true);
}
}
}
}
else
{
if (transferFailed == false)
{
// Message of transfer failed didn't arrive. Timeout exceed.
// Send abort message
this.AbortMoveOperation(ticket,destinationAID,activeRequest.AidOfAgentToMove);
}
// transfer not completed. Restart agent activity.
// the control goes at the end of the method, where
// there is the check of the number of the pending
// requests
this.RestartAgentNotTransferred(activeRequest);
}
}
else
{
this.RestartAgentNotTransferred(activeRequest);
}
break;
}
}
}
else
{
// persistedAgent is not been created. An error during the
// persistence
this.RestartAgentNotTransferred(activeRequest);
}
}
else
{
// transfer is denied. Restart agent activity.
this.RestartAgentNotTransferred(activeRequest);
}
// check if there are any other pending request
lock(this.pendingMoveRequests)
{
count = this.pendingMoveRequests.Count;
}
}
// end of the thread
this.isMoveAgentThreadActive = false;
}
///
/// Thread that check the AMS message queue:
/// - if the queue is empty, it waits to be activated by an
/// incoming message
/// - otherwise it processes the message
///
private void CheckMessages()
{
while (this.bTerminateCMThread == false)
{
// Console.WriteLine("Waiting for an incoming message...");
// wait to be signaled of an incoming message
this.checkMessagesInterruptEvent.WaitOne();
// if the thread has been woken up not because it ended..
if (this.bTerminateCMThread == false)
{
Console.WriteLine("Message arrived...");
// when waoke up, it resets the signaled flag
this.checkMessagesInterruptEvent.Reset();
int count;
lock (this.messageQueue)
{
count = this.messageQueue.Count;
}
while (count > 0)
{
AgentMessage msg = null;
// gets the first message from the queue and removes it
lock (this.messageQueue)
{
msg = this.messageQueue[0];
this.messageQueue.Remove(msg);
}
TransferMessage msgType = (TransferMessage) msg.Body["transferMessage"];
switch (msgType)
{
// the message is a request to transfer a local agent to
// a remote platform
case TransferMessage.MoveRequest:
{
MoveRequest req = msg.Body["moveRequest"] as MoveRequest;
// stop agent activity and change its state in the TransitState
lock (this.agents)
{
int ndx = this.agents.IndexOf(req.AidOfAgentToMove);
if (ndx > -1)
{
Agent.IAgent agent = this.agents[ndx];
agent.Stop();
agent.State = AgentState.Transit;
}
}
// Remove the agent from the active agents list
lock (this.activeAgents)
{
if (this.activeAgents.Contains(req.AidOfAgentToMove))
{
this.activeAgents.Remove(req.AidOfAgentToMove);
}
}
// Add the agent in the transitAgents list
lock (this.transitAgents)
{
this.transitAgents.Add(req.AidOfAgentToMove);
}
// Add the request to transfer the agent to the buffer
lock(this.pendingMoveRequests)
{
this.pendingMoveRequests.Enqueue(req);
}
// check if the moveAgentThread is active
// no : start the thread
// otherwise : continue
if (this.isMoveAgentThreadActive == false)
{
this.moveAgentThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.MoveAgent));
this.moveAgentThread.Name = "moveAgent";
this.moveAgentThread.Start();
}
}
break;
//if the message is a request to receive an agent,
// check the mobility feature of the platform,
// and reply. Set the mobility module in the receive mode
case TransferMessage.Request:
{
TimeSpan timeout = (TimeSpan)msg.Body["timeout"];
int ticket = (int)msg.Body["ticket"];
AID destinationAID = new AID(AID.AMS,msg.Envelope.From.HapName);
MessageBody body = new MessageBody();
body.Add("transferMessage",TransferMessage.ResponseToARequest);
if (this.platform.Description.Mobile == true)
{
body.Add("value",true);
lock (this.receivedTickets)
{
this.receivedTickets.Add(ticket);
}
}
else
{
body.Add("value",false);
}
body.Add("timeout",timeout);
body.Add("ticket",ticket);
AgentMessage replyMessage = new AgentMessage("",body,destinationAID);
replyMessage.Envelope.Date = System.DateTime.Now;
replyMessage.Envelope.From = new AID(AID.AMS,this.platform.Configuration.PlatformDescription.Name);
this.platform.Messaging.SendMessage(replyMessage);
}
break;
// if the message is a response to a previuos request, put it
// in the message queue of the AMS
case TransferMessage.ResponseToARequest:
{
if (this.isMoveAgentThreadActive == true)
{
lock (this.currentMessage)
{
this.currentMessage = msg;
}
this.sourceOfInterrupt = SourceOfInterrupt.ResponseOfTransferRequest;
this.moveAgentInterruptEvent.Set();
}
}
break;
// if the message is the communication of the end of a transfer,
// kill the stopped agent and Remove all agent's structures. To do
// set the flag of transfer succesfull complete and delegate the job to the
// proper thread
case TransferMessage.SuccesfullEnd:
{
if (this.isMoveAgentThreadActive == true)
{
this.sourceOfInterrupt = SourceOfInterrupt.TransferSuccesfull;
this.moveAgentInterruptEvent.Set();
}
}
break;
// if the message is the communication of the end of a transfer,
// kill the stopped agent and Remove all agent's structures. To do
// set the flag of transfer succesfull complete and delegate the job to the
// proper thread
case TransferMessage.UnSuccesfullEnd:
{
if (this.isMoveAgentThreadActive == true)
{
this.sourceOfInterrupt = SourceOfInterrupt.TransferFailed;
this.moveAgentInterruptEvent.Set();
}
}
break;
// the ack for the completing transfer arrived. Execute the
// proper agent
case TransferMessage.SuccesfullAck:
{
// 18-07-2006 : chenged to support application domain
AID receivedAID = (AID)msg.Body["aid"];
string agentName = receivedAID.Name;
bool bDomain = receivedAID.RequireDomain;
string domainName = receivedAID.DomainName;
AID aid = new AID(agentName,this.platform.Configuration.PlatformDescription.Name, bDomain, domainName);
this.Execute(aid);
}
break;
// abort message: the transfer is cancelled.
// clean up the ticket and eventually the agent if
// it is an agent that has to be executed.
case TransferMessage.Abort:
{
int ticket = (int)msg.Body["ticket"];
lock (this.receivedTickets)
{
if (this.receivedTickets.Contains(ticket))
{
this.receivedTickets.Remove(ticket);
}
}
bool agentFound = false;
// changed to support application domain
AID receivedAID = (AID)msg.Body["aid"];
string agentName = receivedAID.Name;
bool bDomain = receivedAID.RequireDomain;
string domainName = receivedAID.DomainName;
AID aid = new AID(agentName,this.platform.Configuration.PlatformDescription.Name, bDomain, domainName);
lock (this.transitAgents)
{
if (this.transitAgents.Contains(aid))
{
this.transitAgents.Remove(aid);
}
agentFound = true;
}
if (agentFound == true)
{
this.Destroy(aid);
}
}
break;
default: break;
}
lock (this.messageQueue)
{
count = this.messageQueue.Count;
}
}
}
}
// when the platform shuts down, the AMS message queue is cleared
this.messageQueue.Clear();
}
#endregion
#region worker methods
///
/// Restarts agent activity after that an agent tried unsuccesfully to
/// move to another platform
///
/// AID of the agent that tried to move
private void RestartAgentNotTransferred (MoveRequest moveRequest)
{
lock (this.transitAgents)
{
int ndx = this.transitAgents.IndexOf(moveRequest.AidOfAgentToMove);
if (ndx > -1)
{
this.transitAgents.Remove(moveRequest.AidOfAgentToMove);
}
else
{
return;
}
}
// if the request to move the agent was originated from the platform
// for load balancing reasons, the agent has to be restarted
if (moveRequest.IsSystemRequest == true)
{
// it's correct
this.Activate(moveRequest.AidOfAgentToMove);
}
// the agent originated the request to move
else
{
AgentService.Agent.AgentPersistent persistedAgent = null;
object handle = this.platform.activeHandle;
try
{
System.Threading.Monitor.Enter(handle);
this.platform.activeAgents++;
}
finally
{
System.Threading.Monitor.Exit(handle);
}
lock (this.agents)
{
int ndx = this.agents.IndexOf(moveRequest.AidOfAgentToMove);
if (ndx > -1)
{
Agent.IAgent agent = this.agents[ndx];
persistedAgent = agent.GetPersistent();
agent.CommitMove(true);
}
}
if (persistedAgent != null)
{
string configFilePath = System.IO.Path.Combine(this.platform.Configuration.BaseDirPath, Platform.AgentsDir);
configFilePath = System.IO.Path.Combine(configFilePath, Platform.AgentsConfSubDir);
string configFile = persistedAgent.ConfigFileName + ".dll";
configFilePath = System.IO.Path.Combine(configFilePath, configFile);
persistedAgent.WriteConfigFile(configFilePath);
this.platform.CreateAgent(persistedAgent.AgentTypeName, persistedAgent.AID.Name,
persistedAgent.ConfigFileName, true, true, persistedAgent.AID.RequireDomain, persistedAgent.AID.DomainName);
try
{
System.Threading.Monitor.Enter(handle);
this.platform.activeAgents--;
}
finally
{
System.Threading.Monitor.Exit(handle);
}
lock (this.activeAgents)
{
this.activeAgents.Add(aid);
}
lock (this.agents)
{
int ndx = this.agents.IndexOf(moveRequest.AidOfAgentToMove);
if (ndx > -1)
{
Agent.IAgent agent = this.agents[ndx];
agent.ResumeFrom(persistedAgent);
agent.State = AgentState.Active;
agent.Activate();
}
}
}
else
{
// error during the persistence. The agent is been eliminated definitely.
}
}
}
///
/// Update the AMS knowledge and persist it to the persistence stream
///
private void updateKnowledge()
{
KnowledgePersistent kp = this.Knowledges[0].GetPersistent();
// CHANGE: 25/01/2006 - index 0:wrong - it's an hashTable
// - index "RegisteredAgents"
// - casted as KnowledgeItem
// - taken its instance
(kp.Items["RegisteredAgents"] as KnowledgeItem).instance = this.registeredAgents;
this.Knowledges[0].ResumeFrom(kp);
this.platform.Persistence.PersistKnowledge(this.AID,kp.Name,kp);
}
///
/// Worker method. Send a message to abort the transfer
/// operation to the remote AMS.
///
/// Ticket identifying the transfer
/// AID of the destination
private void AbortMoveOperation (int ticket, AID destinationAID, AID transferredAgentAID)
{
MessageBody bodyAbort = new MessageBody();
bodyAbort.Add("transferMessage",TransferMessage.Abort);
bodyAbort.Add("ticket",ticket);
bodyAbort.Add("aid",transferredAgentAID);
AgentMessage msgAbort = new AgentMessage("",bodyAbort,destinationAID);
msgAbort.Envelope.Date = System.DateTime.Now;
msgAbort.Envelope.From = new AID(AID.AMS,this.platform.Configuration.PlatformDescription.Name);
this.platform.Messaging.SendMessage(msgAbort);
}
#endregion
///
/// starting the thread to check messages in the local
/// message queue
///
internal void ActivateCheckMessagesThread ()
{
this.checkMessagesInterruptEvent = new ManualResetEvent(false);
// activate the queue of pending requests. Activates here, because it
// is used by the checkMessages thread tu put in it the move requests
if (this.pendingMoveRequests == null)
{
this.pendingMoveRequests = new System.Collections.Queue();
this.isMoveAgentThreadActive = false;
}
else
{
this.pendingMoveRequests.Clear();
}
this.checkMessageQueue = new System.Threading.Thread(new System.Threading.ThreadStart(this.CheckMessages));
this.checkMessageQueue.Name = "checkMessages";
this.checkMessageQueue.Start();
}
#region Override
///
/// Loads agent data from its persisted version
///
/// Persisted version of the agent.
public override void ResumeFrom(AgentPersistent persistent)
{
base.ResumeFrom(persistent);
// resync the internal data with the correponding knowledge
KnowledgePersistent kp = this.Knowledges[0].GetPersistent();
KnowledgeItem item = kp.Items["RegisteredAgents"] as KnowledgeItem;
this.registeredAgents = item.Instance as AgentDescriptionCollection;
}
#endregion
# region Manage Agent State
///
/// Put Agent in active state after transition
///
/// The persisted version of the agent
public void Execute(AID aid)
{
// TODO: the owning platform has changed. Resolvers and addresses:
// change them. This isn't my task.
lock (this.transitAgents)
{
if (this.transitAgents.Contains(aid))
{
this.transitAgents.Remove(aid);
}
else
{
return;
// error; agent not ready to be executed
}
}
lock (this.activeAgents)
{
this.activeAgents.Add(aid);
}
lock (this.agents)
{
int ndx = this.agents.IndexOf(aid);
if (ndx> -1)
{
Agent.IAgent agent = this.agents[ndx];
agent.State = AgentState.Active;
agent.Activate();
}
else
{
// internal error
}
}
}
///
/// Put Agent in the transit state before its migration
///
/// The Agent's AID
/// The name of the destination platform
/// Flag that indicates if the request to move
/// the agent was originated by the platform
/// (i.e. load balancing motivation), or it
/// was originated by the agent itself
public void Move(AID aid, string destination,TimeSpan timeout, bool bIsSystemRequest)
{
// 1. Check the mobility features
bool canBeMoved = this.platform.Description.Mobile == true;
if (canBeMoved == true)
{
// put the agent in the suspended state, untill the transfer is
// finished.(after, the agent will be stopped and then destroyed).
// If anything goes wrong, resume the agent from the suspended state
// i can't suspend the agent here, because the AMS is
// executed in the same thread as the behaviour; so, if
// i suspend the agent, then i suspend the behaviours, and
// then i suspend this method. I suspend the agent after
// i started the moveAgent thread
// this.Suspend(aid);
// create the request
MoveRequest req = new MoveRequest(aid, destination,timeout, bIsSystemRequest);
// Add a message in the AMS message queue, with the request
// to perform a transfer
MessageBody body = new MessageBody();
body.Add("transferMessage",TransferMessage.MoveRequest);
body.Add("moveRequest",req);
AgentMessage msg = new AgentMessage("",body);
lock (this.messageQueue)
{
this.messageQueue.Add(msg);
}
this.checkMessagesInterruptEvent.Set();
// yes : return
}
else
{
// TODO: agent cannot be moved. Log the fact that an agent
// asked to be moved, but that the action is denied
}
}
///
/// Put the Agent in the active state after its suspention
///
/// The Agent's AID
/// true if operation succeds
public bool ResumeAgent(AID aid)
{
int ndx = this.agents.IndexOf(aid);
if (ndx> -1)
{
Agent.IAgent agent = this.agents[ndx];
ndx = this.suspendedAgents.IndexOf(aid);
if(ndx> -1)
{
lock(this.registeredAgents)
{
this.suspendedAgents.Remove(aid);
agent.State = AgentState.Active;
this.activeAgents.Add(aid);
agent.WakeUpAllBehaviours();
ndx = this.registeredAgents.IndexOf(aid);
if (ndx > -1)
{
if (this.registeredAgents[ndx].IsPersistent)
{
bool bOk = this.platform.Persistence.PersistAgent(agent.GetPersistent());
if (bOk == false)
{
// TODO: ERROR!!
}
return bOk;
}
}
}
}
else
{
// TODO: log the fact that the agent is not suspended
}
}
else
{
// TODO: log the fact that the agent is not under the control of ams
}
return false;
}
///
/// Puts Agent in the suspended state
///
/// The Agent's AID
/// true if operation succeds
public bool Suspend(AID aid)
{
lock(this.agents)
{
int ndx = this.agents.IndexOf(aid);
if (ndx> -1)
{
Agent.IAgent agent = this.agents[ndx];
ndx = this.activeAgents.IndexOf(aid);
if (ndx > -1)
{
agent.SuspendAllBehaviours();
this.activeAgents.Remove(aid);
agent.State = AgentState.Suspended;
this.suspendedAgents.Add(aid);
lock(this.registeredAgents)
{
ndx = this.registeredAgents.IndexOf(aid);
if (ndx > -1)
{
if (this.registeredAgents[ndx].IsPersistent)
{
bool bOk = this.platform.Persistence.PersistAgent(agent.GetPersistent());
if (bOk == false)
{
// TODO: ERROR!!!
}
return bOk;
}
}
}
}
else
{
// TODO: log the fact that the agent is not active
}
}
else
{
// TODO: log the fact that the agent is not under the control of ams
}
}
return false;
}
///
/// Suspends all the active agents
///
public void SuspendAll()
{
lock(this.agents)
{
foreach(Agent.Agent agent in this.agents)
{
int ndx = this.activeAgents.IndexOf(agent.AID);
if (ndx > -1)
{
agent.SuspendAllBehaviours();
this.activeAgents.Remove(aid);
agent.State = AgentState.Suspended;
this.suspendedAgents.Add(aid);
lock(this.registeredAgents)
{
ndx = this.registeredAgents.IndexOf(aid);
if (ndx > -1)
{
if (this.registeredAgents[ndx].IsPersistent)
{
bool bOk = this.platform.Persistence.PersistAgent(agent.GetPersistent());
if (bOk == false)
{
// TODO: ERROR!!!
}
}
}
}
}
else
{
// TODO: log the fact that the agent is not active
}
}
}
}
///
/// Stops the agent activity
///
/// agent identifier
///
public bool StopAgent(AID aid)
{
// lock added
lock (this.agents)
{
int ndx = this.agents.IndexOf(aid);
if(ndx>-1)
{
this.agents[ndx].Stop();
return true;
}
}
return false;
}
///
/// Kill the Agent putting it in the unknown state
///
/// the Agent's AID
/// true if operation succeds
public bool Destroy(AID aid)
{
int ndx = this.agents.IndexOf(aid);
if (ndx > -1)
{
Agent.IAgent agent = this.agents[ndx];
agent.SuspendAllBehaviours();
///agent.template.Finalize();
agent.RemoveAllBehaviours();
this.Deregister(aid, false);
return true;
}
else
{
this.platform.Console.WriteError("Agent [Name: {0}] not destroyed. Reason: agent not found.",aid.GlobalName);
}
return false;
}
///
/// Kill all agents putting it in the unknown state
///
public void DestroyAll()
{
lock(this.agents)
{
foreach(Agent.Agent agent in this.agents)
{
agent.SuspendAllBehaviours();
agent.template.Terminate();
agent.RemoveAllBehaviours();
this.Deregister(agent.AID, false);
}
}
}
///
/// Puts Agent in the active state from the waiting state
///
/// The Agent's AID
/// True if operation succeds
public bool WakeUp(AID aid)
{
lock(this.agents)
{
int ndx = this.agents.IndexOf(aid);
if (ndx> -1)
{
Agent.IAgent agent = this.agents[ndx];
ndx = this.waitingAgents.IndexOf(aid);
if (ndx > -1)
{
this.waitingAgents.Remove(aid);
agent.State = AgentState.Active;
this.activeAgents.Add(aid);
agent.WakeUpAllBehaviours();
return true;
}
else
{
// TODO: log the fact that the agent is not waiting
}
}
else
{
// TODO: log the fact that the agent is not under the control of ams
}
return false;
}
}
///
/// Put Agent in the active state from the waiting state
///
public void WakeUpAll()
{
lock(this.agents)
{
foreach(Agent.Agent agent in this.agents)
{
int ndx = this.waitingAgents.IndexOf(aid);
if (ndx > -1)
{
this.waitingAgents.Remove(aid);
agent.State = AgentState.Active;
this.activeAgents.Add(aid);
agent.WakeUpAllBehaviours();
}
}
}
}
# endregion
#region CallBacks
///
/// Handler of messages directed to the AMS
///
/// message
public void DispatchMessage(AgentMessage msg)
{
this.platform.Console.Main.WriteLine("[{0}] {1} - [AMS] : {2}",
this.platform.Description.Name,
System.DateTime.Now,
"Message received from " + msg.Envelope.From.Name);
// puts the message in the AMS message queue if the sender
// is another AMS; otherwise delete the message
if (msg.Envelope.From.Name == AID.AMS)
{
lock (this.messageQueue)
{
this.messageQueue.Add(msg);
}
this.checkMessagesInterruptEvent.Set();
}
else
{
// do nothing
}
}
///
/// Delegate: invoked when the agentTransferred event is raised. It
/// creates the received agent and resumes from the persisted version. The
/// agent is added in the agentsToExecute list. It sends
/// a message to the remote AMS to inform it of completion of the operation,
/// either it is succesfull or not.
///
/// Received persisted version of the agent
/// Ticket owned by the transferred agent. Parameter for an internal check
public void ResumeAgentTransferred (AgentService.Agent.AgentPersistent persistedAgent, int ticket)
{
bool transferAllowed = false;
lock (this.receivedTickets)
{
if (this.receivedTickets.Contains(ticket))
{
this.receivedTickets.Remove(ticket);
transferAllowed = true;
}
}
// if persistedAgent is null, it means that the transfer of the
// agent has not been succesfull
if (transferAllowed == true)
{
MessageBody body = new MessageBody();
if (persistedAgent == null)
{
// inform remote AMS that the transfer is unsuccesfull.
// it's not needed. The remote mobility module will inform
// the remote AMS
}
// inform remote AMS that the transfer is succesfull.
else
{
body.Add("transferMessage",TransferMessage.SuccesfullEnd);
// creo l'agente, mettendolo cos negli agents. Imposto il suo
// stato come transient, e lo pongo nella lista degli agenti
// da execute. L'execute dell'agente (quindi il suo riavvio)
// avverr quando l'AMS remoto d un ack del trasferimento
// configuration file: creates the configuration file
// retrieving informations from persisted agent
// flag of persistence : retrieve informations from
// persisted agent
string configFilePath = System.IO.Path.Combine(this.platform.Configuration.BaseDirPath,Platform.AgentsDir);
configFilePath = System.IO.Path.Combine(configFilePath,Platform.AgentsConfSubDir);
string configFile = persistedAgent.ConfigFileName + ".dll";
configFilePath = System.IO.Path.Combine(configFilePath,configFile);
persistedAgent.WriteConfigFile(configFilePath);
// save the name of the sending platform to use it later
string sendingPlatform = persistedAgent.AID.HapName;
// TODO: the owning platform has changed. Resolvers and addresses:
// change them. This isn't my task.
// MOD : Marco Garr 19/04/2006 : added domain info on AID constructor
AID aid = new AID(persistedAgent.AID.Name,this.platform.Configuration.PlatformDescription.Name,persistedAgent.AID.RequireDomain,persistedAgent.AID.DomainName);
persistedAgent.SetAID(aid);
// TODO: the name of the new agent can be the same of an
// agent alredy existing in the platform. Have i to
// change its name and update all the referencies?
// (Meanwhile i have to change referencies for conversations
// and messages)
// MOD : Marco Garr 19/04/2006 : added domain info on CreateAgent
this.platform.CreateAgent(persistedAgent.AgentTypeName,persistedAgent.AID.Name,
persistedAgent.ConfigFileName,true,true,persistedAgent.AID.RequireDomain, persistedAgent.AID.DomainName);
lock (this.agents)
{
int ndx = this.agents.IndexOf(aid);
if (ndx > -1)
{
AgentService.Agent.IAgent agent = this.agents[ndx];
agent.ResumeFrom(persistedAgent);
this.Resume(agent);
}
else
{
// error: the agent is not created!
}
}
AgentMessage msg = new AgentMessage("",body,new AID(AID.AMS,sendingPlatform));
msg.Envelope.Date = System.DateTime.Now;
msg.Envelope.From = new AID(AID.AMS,this.platform.Configuration.PlatformDescription.Name);
this.platform.Messaging.SendMessage(msg);
}
}
// Something went wrong during the transfer of profile file
else if (ticket == -1)
{
// The ftp client of the remote module
// informs the corresponding AMS
}
else
{
// no such agent is allowed to be resumed
// TODO: log that an agent not expected is trying to move into
// the platform
}
}
#endregion
}
///
/// Various types of messages that AMS can receive during
/// the transfer of an agent
///
public enum TransferMessage
{
///
/// This message is intended for internal use only. It's originated from an
/// agent that wants to transfer itself to a remote platform
///
MoveRequest,
///
/// The message is a response to a previous request to
/// transer an agent
///
ResponseToARequest,
///
/// The message is a request for transfer
/// an agent
///
Request,
///
/// The message indicates the succesfull
/// ending of a transfer
///
SuccesfullEnd,
///
/// The message indicates the unsuccesfull
/// ending of a transfer
///
UnSuccesfullEnd,
///
/// The message indicates the abort of
/// a transfer
///
Abort,
///
/// The message is an ack for the succesfull
/// end of a transfer
///
SuccesfullAck
}
///
/// Source of the interrupt for the moveAgentThread
///
public enum SourceOfInterrupt
{
///
/// Default value. It means that the source of the interrupt
/// isn't set
///
Default,
///
/// The source is the incoming of a succesfull response to a
/// request of transfer
///
ResponseOfTransferRequest,
///
/// The source is the incoming of the message of the succesfull
/// end of a transfer
///
TransferSuccesfull,
///
/// The source is the incoming of the message of the
/// end of a failed transfer
///
TransferFailed
}
}