/*********************************************************************/ /* */ /* 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. */ /* */ /*********************************************************************/ // #define DEBUG_QUEUE using System; using System.Collections.Specialized; using System.IO; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Serialization.Formatters.Soap; using System.Xml; using System.Xml.Serialization; using AgentService.Agent.Runtime; using AgentService.Platform.AMS; using AgentService.Platform.MTS; using System.Runtime.Serialization.Formatters.Binary; namespace AgentService.Platform.Modules.Default { /// /// Class ImprovedMessagingModule. Implementation of the /// IMessagingModule interface with managed agent queues. /// This class is the modified DefaultMessagingModule /// with a more featuring management of threads /// [Serializable()] public class ImprovedMessagingModule : IMessagingModule, IImprovedMessagingModule { /// /// header for the description. /// protected const string descriptionHeader = "Provides an improved messaging service based on file systems serialized queues"; #region Event Handlers /// /// Raises every time a message is sent to the /// AMS queue. /// public event MessageEventHandler NotifyAMS; /// /// Raises every time a message is sent to the /// MTS queue. /// public event MessageEventHandler NotifyMTS; /// /// Raises every time a message is sent to the /// DF queue. /// public event MessageEventHandler NotifyDF; #endregion #region business objects /// /// hashtable mantaining the mapping between the /// agents and the corresponding queues. The hashtable /// is indexed with the aid of the agent and the value /// returned is the collection of messages mantained for /// that agent. /// protected System.Collections.Hashtable queues; /// /// The platform module belonging to /// protected IPlatformContext hapContext; /// /// Module information. /// protected IModuleInfo info; /// /// Installatione and configuration /// informations. /// protected MessagingInfo config; /// /// Serializer object. /// protected MessageQueueSerializer serializer; /// /// configuration file /// protected string configFile; /// /// authentication token used by /// the module to raise events. /// protected int token; /// /// The last error occurred in the module /// protected AgentService.Platform.Modules.ModuleError lastError; /// /// The list of federated platforms. /// private Federation federatedPlatforms; public string TransportProfile { get { string profile = this.hapContext.Description.Name + "@" + this.config.remotingIP + ":" + this.config.remotingPort; return profile; } } /// /// A mediator object used to lock the SendMessage method. /// private object objectForLock; #endregion /// /// Default constructor. /// public ImprovedMessagingModule() { this.federatedPlatforms = new Federation(); } #region IPlatformModule Members /// /// name of the module /// protected string name = "Improved Messaging Module"; /// /// Gets the name of the module /// public virtual string Name { get { return this.name; } } /// /// description of the module /// protected string description = ImprovedMessagingModule.descriptionHeader; /// /// Gets a summary description of the module /// public virtual string Description { get { return this.description; } } /// /// Gets the module category /// public virtual AgentService.Platform.Modules.ModuleCategory Category { get { return AgentService.Platform.Modules.ModuleCategory.Messaging; } } /// /// Gets a bool value indicating if the module is /// able to perform the main functionality of the /// belonging category. If the bool value is false /// the maodule is not a main module. This means that /// in the case of the Messaging Category the module /// can be a filter. If the bool value is true insted /// the module can perform all the base functionalities /// required by the messaging subsystem /// public virtual bool IsMain { get { return true; } } /// /// Checks wheter the module supports features defined in a given interface. /// If the these features are supported the module reference can be subject to /// cast against the requested interface. /// /// full name of the interface that /// true if the specified interface is supported public virtual bool CanSupport(string capabiltyInterface) { return false; } /// /// Returns the last error occurred in the module, null if there /// are no errors to return /// /// Reference to the error. public virtual IModuleError GetLastError() { return this.lastError; } /// /// Attaches the module to the platform. Called by the platform /// when the module is loaded. /// /// reference to the instance of the hosting platform /// information about the module /// returns true is the operation has been successfulland with no errors public virtual bool Attach(IPlatformContext hostingPlatform, ModuleInfo info) { this.objectForLock = true; this.info = info; bool bOk = true; this.hapContext = hostingPlatform; try { this.config = new MessagingInfo(); this.configFile = System.IO.Path.Combine(this.hapContext.PlatformBaseDir, this.hapContext.PlatformCoreConfPath); this.configFile = System.IO.Path.Combine(this.configFile, info.ConfigFile); bOk = this.ReadConfigFile(configFile); if (bOk == true) { this.serializer = new FileMessageQueueSerializer(this.config.dumpPath); if (this.hapContext.IsFirstStart == true) { this.queues = new System.Collections.Hashtable(); } else { bOk = this.DeSerializeQueue(); if (bOk == false) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Cannot deserialize message queues"); } } this.description = string.Format("{0}{1}{1}DumpInterval: {2}{1}DumpPath: {3}{1}EnableEvents: {4}{1}VerboseMode: {5}{1}", ImprovedMessagingModule.descriptionHeader, System.Environment.NewLine, this.config.dumpInterval, this.config.dumpPath, this.config.bEnableEvents, this.config.bVerboseMode); bOk = this.DeSerializeFederationData(); // if there has been any error just recreate // the federation instance. if (bOk == false) { this.federatedPlatforms = new Federation(); // NOTE Christian Vecchiola - Handle the warning. bOk = true; } if (this.config.bEnableRemoteReceive == true) { this.RemotingServer(); } } else { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Cannot read configuration file"); } if (this.config.bEnableEvents == true) { this.token = this.hapContext.GetToken(this); } return bOk; } catch (System.Exception ex) { System.Console.WriteLine(ex.Message + " " + ex.StackTrace); this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Unexpected error in reading configuration file", ex); return false; } } /// /// Detaches the module from the platform Called by the platform when /// the module is unloaded /// /// true if the operation has been successful public virtual bool Detach() { ChannelServices.UnregisterChannel(this.channel); this.serializer.Serialize(this.queues); this.queues = null; this.SerializeFederationData(); this.federatedPlatforms = null; return true; } /// /// Installs a the module in the platform. Called by the pltaform when the module is /// installed. /// /// platform accessible data /// configuration file /// installation info object used to store install information /// true if the installation process has been successful public virtual bool Install(IPlatformContext hostingPlatform, ModuleInfo info, ref InstallationInfo instInfo) { this.hapContext = hostingPlatform; this.info = info; try { this.configFile = System.IO.Path.Combine(this.hapContext.PlatformBaseDir, this.hapContext.PlatformCoreConfPath); this.configFile = System.IO.Path.Combine(configFile, this.info.ConfigFile); this.config = new MessagingInfo(); if(File.Exists(this.configFile)) { this.ReadConfigFile(this.configFile); } this.config.federationConfigFile = Path.Combine(this.hapContext.PlatformCoreConfPath, MessagingInfo.DefaultFederationConfigFile); this.WriteConfigFile(); if (Directory.Exists(this.config.dumpPath) == false) { Directory.CreateDirectory(this.config.dumpPath); } instInfo = this.config.Clone() as MessagingInfo; return true; } catch (System.IO.IOException ioex) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Unexpected IO error", ioex); return false; } } /// /// Uninstall the module. Called by the platform when the module is removed permanently /// from the platform /// /// installation info object used to install the module /// true if the uninstall process has been successfull public virtual bool UnInstall(InstallationInfo instInfo) { MessagingInfo mInfo = instInfo as MessagingInfo; if (mInfo != null) { try { System.IO.Directory.Delete(mInfo.dumpPath, true); return true; } catch (System.IO.IOException ioex) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not delete queues directory", ioex); return false; } } else { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Unable to retrieve installation data"); return false; } } #endregion #region IMessagingModule Members /// /// Creates a message queue owned by the given agent. /// /// agent identifier of the agent that owns the queue /// true if the queue has been successfully created public virtual bool CreateAgentQueue(AID aid) { if (this.queues[aid.Name] != null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue already exists"); return false; } this.lastError = null; QueueItemManager item = new QueueItemManager(new MessageQueueMapping(aid, new AgentMessageCollection())); this.queues.Add(aid.Name, item); if (this.config.bEnableEvents == true) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageBatch, MessageVerb.CreateQueue, null, aid); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageBatch, args, this.token); } return true; } /// /// Removes a message queue owned by the given agent. /// /// agent identifier of the agent that owns the queue /// true if the queue has been successfully removed public virtual bool RemoveAgentQueue(AID aid) { //MessageQueueMapping mapping = this.queues[aid.Name] as MessageQueueMapping; QueueItemManager item = this.queues[aid.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return false; } this.lastError = null; this.queues.Remove(aid.Name); MessageQueueMapping mapping = item.QueueMapping; bool bOk = this.serializer.Serialize(new MessageQueueMapping(mapping.Owner, null)); if (bOk == false) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not Remove agent queue"); } if (this.config.bEnableEvents == true) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageBatch, MessageVerb.RemoveQueue, null, aid); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageBatch, args, this.token); } return bOk; } /// /// Checks whether a queue has messages. /// /// agent identifier of the owner of the queue /// true if the queue has messages public virtual bool HasMessages(AID aid) { // AgentMessageCollection queue = this.queues[aid.Name] as AgentMessageCollection; //MessageQueueMapping mapping = this.queues[aid.Name] as MessageQueueMapping; QueueItemManager item = this.queues[aid.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return false; } MessageQueueMapping mapping = item.QueueMapping; this.lastError = null; if ((this.config.bEnableEvents == true) && (this.config.bVerboseMode == true)) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageBatch, MessageVerb.Query, null, aid); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageBatch, args, this.token); } return (mapping.Queue.Count > 0); } /// /// Sends a message to an agent. /// /// message /// true if the message has been successfully sent public virtual bool SendMessage(AgentService.Platform.MTS.AgentMessage message) { bool bOk = true; this.lastError = null; //lock (this.objectForLock) // Maybe it must to add a lock in order to manage concurrent accesses to the queue... // ...but if we add the lock the message module doesn't work! Inexplicable! foreach (AID aid in message.Envelope.To) { if (this.DispatchToCoreAgents(aid, message) == false) { //AgentMessageCollection queue = this.queues[aid.Name] as AgentMessageCollection; //MessageQueueMapping mapping = this.queues[aid.Name] as MessageQueueMapping; QueueItemManager item = this.queues[aid.Name] as QueueItemManager; // NOTE: Christian Vecchiola - better to use CompareNoCase ? if (item == null || string.Compare(aid.HapName, this.hapContext.Description.Name) != 0) { if (this.config.bEnableRemoteSend == true) { try { //Console.WriteLine("---->SendMessage"); this.RemotingClient(message, aid); return true; } catch (RemotingSendException rse) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exist in this application. " + rse.Message); return false; } catch (Exception e) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists. " + e.Message); return false; } } this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Remote send is not enabled"); return false; } else if (item != null) { MessageQueueMapping mapping = item.QueueMapping; mapping.Queue.Add(message); this.RaiseEvent(aid, message); #if DEBUG_QUEUE this.DbgHelp("SendMessage", "ADD", message); #endif mapping.Changes++; if (mapping.Changes >= this.config.dumpInterval) { bOk = bOk && this.serializer.Serialize(mapping); if (bOk == false) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not serialize agent queue"); } } } else { } // System.Console.WriteLine("[Messaging] Sent message to queue {0} sender: {1}", aid.Name, message.Envelope.From.Name); } if (this.config.bEnableEvents == true) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageSent, MessageVerb.Send, message, aid); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageSent, args, this.token); } } return bOk; } /// /// Receives a message pops it from the queue. /// /// agent identifier of the agent that owns the queue /// the first message available in the queue public virtual AgentService.Platform.MTS.AgentMessage ReceiveMessage(AID aid) { // AgentMessageCollection queue = this.queues[aid.Name] as AgentMessageCollection; //MessageQueueMapping mapping = this.queues[aid.Name] as MessageQueueMapping; QueueItemManager item = this.queues[aid.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return null; } MessageQueueMapping mapping = item.QueueMapping; this.lastError = null; if (mapping.Queue.Count > 0) { AgentMessage msg = mapping.Queue[0]; mapping.Queue.Remove(msg); #if DEBUG_QUEUE this.DbgHelp("ReceiveMessage", "REM", msg); #endif mapping.Changes++; // System.Console.WriteLine("[Messaging] Popped message from queue {0} sender: {1}", aid.Name, msg.Envelope.From.Name); if (mapping.Changes >= this.config.dumpInterval) { bool bOk = this.serializer.Serialize(mapping); if (bOk == false) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not serialize agent queue"); } } if (this.config.bEnableEvents == true) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageReceived, MessageVerb.Receive, msg, aid); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageReceived, args, this.token); } return msg; } return null; } /// /// Retrieves all the messages and leaves them in the queue. /// /// agent identifier of the agent that owns the queue /// message filter, set this parameter to null if you do not want to applay any filter /// max number or message to retrieve /// if true removes the message from the queue /// the collection of messages retrieved from the queue public virtual AgentService.Platform.MTS.AgentMessageCollection GetMessages(AID aid, MessageFilter filter, int max, bool bRemove) { // AgentMessageCollection queue = this.queues[aid.Name] as AgentMessageCollection; //MessageQueueMapping mapping = this.queues[aid.Name] as MessageQueueMapping; QueueItemManager item = this.queues[aid.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return null; } MessageQueueMapping mapping = item.QueueMapping; this.lastError = null; AgentMessageCollection newColl = new AgentMessageCollection(mapping.Queue.Count); AgentMessageCollection queue = mapping.Queue; for (int ndx = 0; ndx < queue.Count; ndx++) { AgentMessage msg = queue[ndx]; if ((filter == null) || (filter.Filter(msg) == true)) { newColl.Add(msg); if (bRemove == true) { queue.Remove(msg); #if DEBUG_QUEUE this.DbgHelp("GetMessages", "REM", msg); #endif ndx--; } max--; if (max == 0) { break; } } } if ((this.config.bEnableEvents == true) && (this.config.bVerboseMode == true)) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageBatch, MessageVerb.GetAll, null, aid); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageBatch, args, this.token); } return newColl; } /// /// Retrieves all the messages and leaves them in the queue. /// /// agent identifier of the agent that owns the queue /// message filter, set this parameter to null if you do not want to applay any filter /// if true removes the message from the queue /// the first message retrieved from the queue, satisfying the filter public virtual AgentService.Platform.MTS.AgentMessage GetMessage(AID aid, MessageFilter filter, bool bRemove) { //MessageQueueMapping mapping = this.queues[aid.Name] as MessageQueueMapping; QueueItemManager item = this.queues[aid.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return null; } this.lastError = null; MessageQueueMapping mapping = item.QueueMapping; AgentMessageCollection queue = mapping.Queue; AgentMessage msg = null; for (int ndx = 0; ndx < queue.Count; ndx++) { msg = queue[ndx]; if ((filter == null) || (filter.Filter(msg) == true)) { if (bRemove == true) { queue.Remove(msg); ndx--; #if DEBUG_QUEUE this.DbgHelp("GetMessage","REM",msg); #endif } } else { msg = null; } break; } if ((this.config.bEnableEvents == true) && (this.config.bVerboseMode == true)) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageReceived, MessageVerb.Peek, null, null); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageReceived, args, this.token); } return msg; } /// /// Removes the given message from the queue. /// /// agent identifier of the agent that owns the queue /// message to be destroyed /// true if message is successfully removed public virtual bool RemoveMessage(AID aid, AgentMessage message) { bool bOk = false; // AgentMessageCollection queue = this.queues[aid.Name] as AgentMessageCollection; //MessageQueueMapping mapping = this.queues[aid.Name] as MessageQueueMapping; QueueItemManager item = this.queues[aid.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return bOk; } MessageQueueMapping mapping = item.QueueMapping; int ndx = -1; if ((ndx = mapping.Queue.IndexOf(message)) > -1) { this.lastError = null; mapping.Queue.Remove(mapping.Queue[ndx]); #if DEBUG_QUEUE this.DbgHelp("RemoveMessage", "REM", message); #endif // System.Console.WriteLine("[Messaging] Removed message from queue {0} sender: {1}", aid.Name, message.Envelope.From.Name); mapping.Changes++; if (mapping.Changes >= this.config.dumpInterval) { bOk = this.serializer.Serialize(mapping); if (bOk == false) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not serialize agent queue"); } } if (this.config.bEnableEvents == true) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageBatch, MessageVerb.Remove, message, aid); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageBatch, args, this.token); } return bOk; } else { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "The given message is not present in the queue"); return bOk; } } /// /// Creates a client of the messaging module for the given agent. /// /// agent identifier. /// a messaging client for the given agent, null if the module does not provide a custom client. public virtual AgentService.Agent.Runtime.IMsgClient CreateClient(AID aid) { return new ImprovedMsgClient(this); } /// /// Returns true if the messaging module provides a custom client. /// public virtual bool HasClient { get { return true; } } /// /// Removes all the messages in each agent queue /// /// True if succeds public virtual void ClearAllMessages() { foreach (QueueItemManager item in this.queues.Values) { MessageQueueMapping mapping = item.QueueMapping; mapping.Queue.Clear(); } this.lastError = null; this.SerializeQueue(); if (this.config.bEnableEvents == true) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageBatch, MessageVerb.ClearAll, null, null); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageBatch, args, this.token); } } /// /// Removes all the messages from the queue of the given agent /// /// AID of the agent /// True if succeds public virtual bool ClearAllMessages(AID aid) { bool bOk = false; // AgentMessageCollection amc = this.queues[aid] as AgentMessageCollection; QueueItemManager item = this.queues[aid.Name] as QueueItemManager; if (item == null) { MessageQueueMapping mapping = item.QueueMapping; this.lastError = null; mapping.Queue.Clear(); mapping.Changes++; if (mapping.Changes >= this.config.dumpInterval) { bOk = this.serializer.Serialize(mapping); if (bOk == false) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Cannot serialize agent queue"); } } if (this.config.bEnableEvents == true) { MessageEventArgs args = new MessageEventArgs(PlatformEvent.MessageBatch, MessageVerb.ClearQueue, null, aid); this.hapContext.RaiseStandardEvent(PlatformEvent.MessageBatch, args, this.token); } return bOk; } this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not find queue for " + aid.Name); return bOk; } #endregion #region Worker Methods /// /// Checks if the message is directed to a service agent /// (AMS, MTS, DF) and forwards the message if true. /// /// message /// true if the message hhas been dispatched to a service agent, false otherwise private bool DispatchToCoreAgents(AID aid, AgentMessage message) { if ((aid.Name == AID.AMS) && (this.NotifyAMS != null)) { this.NotifyAMS(message); return true; } else if ((aid.Name == AID.MTS) && (this.NotifyMTS != null)) { this.NotifyMTS(message); return true; } else if ((aid.Name == AID.DF) && (this.NotifyDF != null)) { this.NotifyDF(message); //return true; } return false; } /// /// Sezializes the message queue. /// /// true if serialization is successful private bool SerializeQueue() { return this.serializer.Serialize(this.queues); } /// /// Deserializes the message queue. /// /// true if successful private bool DeSerializeQueue() { return this.serializer.DeSerialize(ref this.queues); } /// /// Reads the configuration file of the module. /// /// configuration file /// true if successful protected bool ReadConfigFile(string configFile) { bool bReadOk = true; using (StreamReader sr = new StreamReader(configFile)) { string line = string.Empty; while ((line = sr.ReadLine()) != null) { if ((line.StartsWith("#") == false) && (line != string.Empty)) { int ndxSeparator = line.IndexOf(' '); string property = line.Substring(0, ndxSeparator).Trim(); string value = line.Substring(ndxSeparator).Trim(); // TODO: read data file. switch (property) { case "DumpInterval": this.config.dumpInterval = int.Parse(value); break; case "DumpPath": if (System.IO.Path.IsPathRooted(value) == false) { this.config.dumpPath = System.IO.Path.Combine(this.hapContext.PlatformBaseDir, value); } else { this.config.dumpPath = value; } break; case "EnableEvents": this.config.bEnableEvents = bool.Parse(value); break; case "VerboseMode": this.config.bVerboseMode = bool.Parse(value); break; case "FederationConfig": this.config.federationConfigFile = value; break; case "EnableRemoteReceive": this.config.bEnableRemoteReceive = bool.Parse(value); break; case "EnableRemoteSend": this.config.bEnableRemoteSend = bool.Parse(value); break; case "RemotingPort": this.config.remotingPort = int.Parse(value); break; case "RemotingIP": this.config.remotingIP = value; break; } } } } return bReadOk; } /// /// Writes data to the configuration file. /// protected void WriteConfigFile() { FileStream fs = new FileStream(this.configFile, FileMode.Create); using (StreamWriter sw = new StreamWriter(fs)) { sw.WriteLine("# DumpInterval: the number of maximum changes on an agent message queue between two queue serializaions"); sw.WriteLine("#"); sw.WriteLine("DumpInterval {0}", this.config.dumpInterval); sw.WriteLine(); sw.WriteLine("# DumpPath: the path to the serialization directory"); sw.WriteLine("#"); sw.WriteLine("DumpPath {0}", this.config.dumpPath); sw.WriteLine(); sw.WriteLine("# EnableEvents: boolean flag that is used to control the event firing for this module"); sw.WriteLine("# if true, events are risen at each operation on the module"); sw.WriteLine("#"); sw.WriteLine("EnableEvents {0}", this.config.bEnableEvents); sw.WriteLine(); sw.WriteLine("# VerboseMode: boolean flag that is used to control which events are fired. If true all"); sw.WriteLine("# the events are fired and also those which can occur frequently (GetAll, Peek, and Query verbs)"); sw.WriteLine("#"); sw.WriteLine("VerboseMode {0}", this.config.bVerboseMode); sw.WriteLine(); sw.WriteLine("# FederationConfig: the file containing the federated platforms"); sw.WriteLine("#"); sw.WriteLine("FederationConfig {0}", this.config.federationConfigFile); sw.WriteLine(); sw.WriteLine("# EnableRemoteSend: boolean flag that is used to activate the feature of sending messages to other platforms."); sw.WriteLine("#"); sw.WriteLine("EnableRemoteSend {0}", this.config.bEnableRemoteSend); sw.WriteLine(); sw.WriteLine("# EnableRemoteReceive: boolean flag that is used to activate the feature of sending messages to other platforms."); sw.WriteLine("#"); sw.WriteLine("EnableRemoteReceive {0}", this.config.bEnableRemoteReceive); sw.WriteLine(); sw.WriteLine("# RemotingPort: the port where the remoting server is listening"); sw.WriteLine("#"); sw.WriteLine("RemotingPort {0}", this.config.remotingPort); sw.WriteLine(); sw.WriteLine("# RemotingIP: the IP where the remoting server is listening"); sw.WriteLine("#"); sw.WriteLine("RemotingIP {0}", this.config.remotingIP); sw.WriteLine(); } } /// /// This method updates the list of federated platforms. /// private bool DeSerializeFederationData() { //Console.WriteLine("---->"+this.hapContext.PlatformBaseDir + "\\" + this.config.federationConfigFile); if (System.IO.File.Exists(this.hapContext.PlatformBaseDir+"\\"+this.config.federationConfigFile) == true) { XmlSerializer serializer = new XmlSerializer(typeof(Federation)); XmlReader reader = null; try { reader = XmlReader.Create(this.hapContext.PlatformBaseDir + "\\" + this.config.federationConfigFile); this.federatedPlatforms = (Federation)serializer.Deserialize(reader); return true; } finally { if (reader != null) { reader.Close(); } } } return false; } /// /// Serializes the federated platform list in the usual configuration file. /// private void SerializeFederationData() { XmlSerializer serializer = new XmlSerializer(typeof(Federation)); XmlWriter writer = null; try { //string file = System.IO.Directory.GetCurrentDirectory() + this.config.federationConfigFile; //System.Console.WriteLine("----------------------" + file);//+ this.config.federationConfigFile); writer = XmlWriter.Create(this.hapContext.PlatformBaseDir + "\\" + this.config.federationConfigFile); serializer.Serialize(writer, this.federatedPlatforms); } finally { if (writer != null) { writer.Close(); } } } private HttpChannel channel; private void RemotingServer() { try { ListDictionary channelProperties = new ListDictionary(); channelProperties.Add("port", this.config.remotingPort); //SoapServerFormatterSinkProvider so = new SoapServerFormatterSinkProvider(); BinaryServerFormatterSinkProvider bi = new BinaryServerFormatterSinkProvider(); //so.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full; bi.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full; bool found = false; foreach (IChannel ch in ChannelServices.RegisteredChannels) { HttpChannel httpCh = ch as HttpChannel; //Console.WriteLine("---->"); if (httpCh != null) { if (((int)httpCh.Properties["port"]) == this.config.remotingPort) { //Console.WriteLine("---->"+httpCh.Properties["port"].ToString()); found = true; break; } } } if (!found) { //Console.WriteLine("---->"); channel = new HttpChannel(channelProperties, new BinaryClientFormatterSinkProvider(), bi); ChannelServices.RegisterChannel(channel, false); } RemotingSend send = new RemotingSend(); RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingSend), "send.soap", WellKnownObjectMode.Singleton); ObjRef objref = RemotingServices.Marshal(send); IRemotingSend rs = (IRemotingSend)Activator.GetObject(typeof(RemotingSend), "http://localhost:" + this.config.remotingPort + "/send.soap"); RemotingMediator mediator = new RemotingMediator(this); rs.MessageReceived += new MessageReceivedEventHandler(mediator.GetRemoteMessage); /*FileStream filestream = new FileStream(this.hapContext.PlatformCoreConfPath + "send.soap", FileMode.Create); /*SoapFormatter soapformatter = new SoapFormatter(); soapformatter.FilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Low; soapformatter.Serialize(filestream, objref);*/ /*BinaryFormatter binaryformatter = new BinaryFormatter(); binaryformatter.FilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Low; binaryformatter.Serialize(filestream, objref); filestream.Close();*/ //System.Console.WriteLine("Cioa"); } catch (Exception e) { System.Console.WriteLine(e.StackTrace); } } /// /// Private method which sends the message to the remote agent, if its platform is /// federated with this one. /// /// The message to send /// The aid of the receiver agent private void RemotingClient(AgentMessage message, AID aid) { bool found = false; // NOTE - Christian Vecchiola: Why we nned to Always deserialize the // federation from disk? // this.DeSerializeFederationData(); //Console.WriteLine("----->PlatformS"); foreach (RemotePlatform pl in this.federatedPlatforms.Platforms) { if (pl.Name == aid.HapName) { //Console.WriteLine("----->Platform"); found = true; this.SendToRemotePlatform(message, pl); return; } } if (found == false) { throw new RemotingSendException("Unable to find the federated platform"); } } /// /// Private method which sends the message to the remote platform /// /// The message to send. /// The remote platform. private void SendToRemotePlatform(AgentMessage message, RemotePlatform rp) { try { IRemotingSend send = (IRemotingSend)Activator.GetObject(typeof(IRemotingSend), "http://" + rp.IP + ":" + rp.Port + "/send.soap"); send.SendMessage(message); } catch (Exception e) { throw new RemotingSendException("Connection failed due to this exception: " + e.Message); } } #endregion /// /// This method allows an external Message Client to register itself /// to the event raised when a message is added to a queue. /// /// The delegate /// The AID of the agent that waits for messages. public void RegisterToIncomingMessage(IncomingMessage messageIncoming, AID agent) { QueueItemManager item = this.queues[agent.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return; } item.OnIncomingMessage += messageIncoming; this.lastError = null; } /// /// This method allows an external Message Client to register itself /// to the event raised when an conversation request is incoming. /// /// The delegate /// The agent which is receiving the conversation request. public void RegisterToIncomingConversation(IncomingConversation conversationIncoming, AID agent) { QueueItemManager item = this.queues[agent.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return; } item.OnIncomingConversation += conversationIncoming; this.lastError = null; } /// /// This method allows an external Message Client to register itself /// to the event raised when an conversation message is incoming. /// /// The delegate /// The agent AID. public void RegisterToIncomingConversationMessage(IncomingConversationMessage conversationIncoming, AID agent) { QueueItemManager item = this.queues[agent.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return; } item.OnIncomingConversationMessage += conversationIncoming; this.lastError = null; } /// /// This method allows an external Message Client to deregister itself /// to the event raised when a message is added to a queue. /// /// The delegate /// The AID of the agent that waits for messages. public void DeregisterToIncomingMessage(IncomingMessage messageIncoming, AID agent) { QueueItemManager item = this.queues[agent.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return; } item.OnIncomingMessage -= messageIncoming; this.lastError = null; } /// /// This method allows an external Message Client to register itself /// to the event raised when an conversation request is incoming. /// /// The delegate /// The agent which is receiving the conversation request. public void DeregisterToIncomingConversation(IncomingConversation conversationIncoming, AID agent) { QueueItemManager item = this.queues[agent.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return; } item.OnIncomingConversation += conversationIncoming; this.lastError = null; } /// /// This method allows an external Message Client to register itself /// to the event raised when an conversation message is incoming. /// /// The delegate /// The agent AID. public void DeregisterToIncomingConversationMessage(IncomingConversationMessage conversationIncoming, AID agent) { QueueItemManager item = this.queues[agent.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return; } item.OnIncomingConversationMessage -= conversationIncoming; this.lastError = null; } /// /// Raises an event when a new message is added to an agent queue. /// /// The aid of the agent recipient /// The incoming message private void RaiseEvent(AID agentAID, AgentMessage message) { ConversationBody body = message.Body as ConversationBody; if (body != null && (body.Verb == ConversationProtocol.Propose || body.Verb == ConversationProtocol.Close)) { QueueItemManager item = this.queues[agentAID.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return; } item.RaiseIncomingConversationEvent(agentAID); } else if (message.ConversationId.Equals("ServiceMessage") == false && message.ConversationId.Equals("SimpleMessage") == false) { QueueItemManager item = this.queues[agentAID.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return; } item.RaiseIncomingConversationMessageEvent(message.ConversationId, agentAID); } else { QueueItemManager item = this.queues[agentAID.Name] as QueueItemManager; if (item == null) { this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Agent queue does not exists"); return; } item.RaiseIncomingMessageEvent(agentAID); } } #if DEBUG_QUEUE #region Debug Help /// /// Debug method. Writes to the console each operation /// performed on an agent message queue, along with /// some interesting details about the message. /// /// name of the calling method /// operation name /// message protected void DbgHelp(string methodName, string verb, AgentMessage msg) { Agent.Runtime.ConversationBody cb = msg.Body as Agent.Runtime.ConversationBody; string text = (cb != null ? String.Format("<{0},{1}:{2}:{3}>",cb.Verb,cb.OriginalId,cb.CandidateId,cb.CandidateId) : "common"); Console.WriteLine("MODULE({5}) - [{4}::{0}->{1}] - Msg({2},{3})", msg.Envelope.From.Name, msg.Envelope.To[0].Name, msg.ConversationId, text, verb, methodName); } #endregion #endif } }