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