/*********************************************************************/ /* */ /* 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 } }