/*********************************************************************/
/* */
/* 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.IO;
using System.Collections;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using AgentService.Agent.Knowledge;
using AgentService.Agent.Runtime;
using AgentService.Platform.AMS;
using AgentService.Platform.Modules;
using AgentServiceDefaultModule;
namespace AgentService.Platform.Modules.Default
{
///
/// Class PersistenceInfo. Holds installation information for the persistence module.
///
[Serializable]
public class PersistenceInfo : InstallationInfo
{
///
/// base directory for the persistence module
///
public string BaseDir;
///
/// mapping file for the persistence module
///
public string MapFile;
///
/// Constructs a PersistenceInfo object.
///
/// base directory for the persistence
/// mapping file for the persistence
public PersistenceInfo(string baseDirectory, string mapFile)
{
this.BaseDir = baseDirectory;
this.MapFile = mapFile;
}
}
///
/// Class DefaultPersistenceModule. Default implementation of the IPersistenceModule interface.
///
public class DefaultPersistenceModule : IPersistenceModule
{
#region DefaultPersistenceModule Member
private static string PlatformMapFile = "platform.map";
private static string AgentMapFile = "agent.map";
private static string SerializationExt = ".dat";
private static string AgentDataFile = "agent.dat";
///
/// Root directory of the persistence file system
///
private string persistenceRoot;
///
/// File that Contains mapping between agents' AID and their
/// persisted informations
///
private string mapFile;
///
/// Hashtable containing mappings between AIDs and persistence info
///
private Hashtable mappings;
///
/// Last error occurred in the module.
///
private ModuleError lastError;
///
/// Information about the module
///
private ModuleInfo info;
///
/// Constructs a DefaultPersistenceModule object.
///
public DefaultPersistenceModule()
{
this.mappings = new Hashtable();
}
# endregion
#region IPlatformModule Members
///
/// name of the module
///
private string name = "Default Persistence Module";
///
/// Gets the name of the module
///
public string Name
{
get
{
return this.name;
}
}
///
/// description of the module
///
private string description = "Provides a default implementation of the persistence subsystem";
///
/// Gets a summary description of the module
///
public string Description
{
get
{
return this.description;
}
}
///
/// Gets the module category
///
public AgentService.Platform.Modules.ModuleCategory Category
{
get
{
return AgentService.Platform.Modules.ModuleCategory.Persistence;
}
}
///
/// 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 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 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 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 bool Attach(IPlatformContext hostingPlatform, ModuleInfo info)
{
System.IO.FileStream fs = null;
try
{
string configFile = System.IO.Path.Combine(hostingPlatform.PlatformBaseDir, hostingPlatform.PlatformCoreConfPath);
configFile = System.IO.Path.Combine(configFile, info.ConfigFile);
this.readData(configFile);
fs = new System.IO.FileStream(System.IO.Path.Combine(this.persistenceRoot,this.mapFile), FileMode.Open);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Binder = new ASBinder();
this.mappings = formatter.Deserialize(fs) as System.Collections.Hashtable;
}
catch (System.Runtime.Serialization.SerializationException se)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not read serialization file", se);
return false;
}
catch(System.IO.IOException ioex)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not read configuration file", ioex);
return false;
}
catch(System.Exception e)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Generic error occurred", e);
return false;
}
finally
{
fs.Close();
}
return true;
}
///
/// Detaches the module from the platform Called by the platform when
/// the module is unloaded
///
/// true if the operation has been successful
public bool Detach()
{
return this.doSerializeMappings();
}
///
/// Installs a the module in the platform. Called by the pltaform when the module is
/// installed.
///
/// platform accessible data
/// information about the module
/// installation info object used to store install information
/// true if the installation process has been successful
public bool Install(IPlatformContext hostingPlatform, ModuleInfo info, ref InstallationInfo instInfo)
{
int msg=0;
this.info = info;
try
{
string configFile = System.IO.Path.Combine(hostingPlatform.PlatformBaseDir, hostingPlatform.PlatformCoreConfPath);
configFile = System.IO.Path.Combine(configFile, info.ConfigFile);
if (System.IO.File.Exists(configFile) == false)
{
// ok generate default configuration file
System.IO.StreamWriter writer = new System.IO.StreamWriter(configFile);
this.persistenceRoot = System.IO.Path.Combine(hostingPlatform.PlatformBaseDir,"persistence");
writer.WriteLine(this.persistenceRoot);
this.mapFile = DefaultPersistenceModule.PlatformMapFile;
writer.WriteLine(this.mapFile);
writer.Close();
}
this.readData(configFile);
this.mapFile = System.IO.Path.GetFileName(this.mapFile);
System.IO.Directory.CreateDirectory(this.persistenceRoot);
this.mappings = new Hashtable();
instInfo = new PersistenceInfo(this.persistenceRoot, this.mapFile);
return this.doSerializeMappings();
}
catch(System.IO.IOException ex)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, String.Format("Could not read configuration file : {0}", msg), ex);
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 bool UnInstall(InstallationInfo instInfo)
{
PersistenceInfo pInfo = instInfo as PersistenceInfo;
try
{
System.IO.Directory.Delete(pInfo.BaseDir,true);
}
catch(System.IO.IOException ioex)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not delete persistence storage", ioex);
return false;
}
return true;
}
#endregion
#region IPersistenceModule Members
///
/// Persists the given software agent.
///
/// agent persistent data
/// true if successful
public bool PersistAgent(AgentService.Agent.AgentPersistent agent)
{
string path;
object obj = this.mappings[agent.AID.GlobalName];
string agentDir = obj as string;
if (agentDir == null)
{
// Add the serialization information and
// create a new directory for the new agent
string dirName = String.Format("__{0}_{1}{2}",agent.AID.Name, agent.AID.HapName,System.IO.Path.DirectorySeparatorChar);
this.mappings.Add(agent.AID.GlobalName,dirName);
if (this.doSerializeMappings() == false)
{
return false;
}
// create the new directory
path = System.IO.Path.Combine(this.persistenceRoot,dirName);
System.IO.Directory.CreateDirectory(path);
}
else
{
path = System.IO.Path.Combine(this.persistenceRoot,agentDir);
}
System.IO.StreamWriter sw = null;
string file = System.IO.Path.Combine(path,DefaultPersistenceModule.AgentMapFile);
try
{
// creates the .map file
sw = new System.IO.StreamWriter(file,false);
foreach(KnowledgePersistent kp in agent.PersistedKnowledges)
{
sw.WriteLine(kp.Name);
string kPath = System.IO.Path.Combine(path, kp.Name + DefaultPersistenceModule.SerializationExt);
if (this.doPersistKnowledge(kp, kPath) == false)
{
return false;
}
}
}
catch(System.IO.IOException ioex)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Cannot persist agent " + agent.AID.GlobalName, ioex);
return false;
}
finally
{
sw.Close();
}
file = System.IO.Path.Combine(path,DefaultPersistenceModule.AgentDataFile);
System.IO.FileStream fs = null;
BinaryFormatter formatter = new BinaryFormatter();
formatter.Binder = new ASBinder();
try
{
fs = new System.IO.FileStream(file, FileMode.Create);
formatter.Serialize(fs, agent);
}
catch (System.Runtime.Serialization.SerializationException e)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not write to serialization file", e);
return false;
}
finally
{
if (fs != null)
{
fs.Close();
}
}
this.lastError = null;
return true;
}
///
/// Return false
///
public bool HasClient
{
get
{
// TODO: Add DefaultPersistenceModule.HasClient getter implementation
return false;
}
}
///
/// Gets the persistence client of the given module for the given agent.
///
/// identifier of the agent
/// persistence client
/// returns the DefaultPersistenceClient
public AgentService.Agent.Runtime.IPersistenceClient CreateClient(AID aid)
{
DefaultPersistenceClient client = new DefaultPersistenceClient();
return client;
}
///
/// Removes from the storage the persisted data of the
/// given agent.
///
/// agent identifier
/// true if successful
public bool RemoveAgent(AID aid)
{
string dirName = this.mappings[aid.GlobalName] as string;
if (dirName != null)
{
try
{
string path = System.IO.Path.Combine(this.persistenceRoot,dirName);
System.IO.Directory.Delete(path,true);
this.mappings.Remove(aid.GlobalName);
return this.doSerializeMappings();
}
catch(System.IO.IOException ioex)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not completely Remove agent " + aid.GlobalName, ioex);
return false;
}
}
else
{
return false;
}
}
///
/// Persists the data of the given knowledge information for the given agent.
///
/// agent identifer
/// name of the knowledge object
/// knowledge persisted data
/// true if successful
public bool PersistKnowledge(AID aid, string name, AgentService.Agent.Knowledge.KnowledgePersistent knowledgePersistent)
{
object value = this.mappings[aid.GlobalName];
if (value != null)
{
// read the .map file and update the knowledge items
string basePath = System.IO.Path.Combine(this.persistenceRoot,(value as string));
System.IO.StreamReader reader = new System.IO.StreamReader(System.IO.Path.Combine(basePath,DefaultPersistenceModule.AgentMapFile));
// skip the type name
reader.ReadLine();
string knowledgeList = reader.ReadToEnd();
string[] knowledges = knowledgeList.Split('\n');
bool bFound = false;
foreach(string k_encoded in knowledges)
{
if (k_encoded.StartsWith(name) == true)
{
// ok found...
bFound = true;
break;
}
}
reader.Close();
string kPath = System.IO.Path.Combine(basePath,knowledgePersistent.Name + DefaultPersistenceModule.SerializationExt);
if (this.doPersistKnowledge(knowledgePersistent, kPath) == false)
{
return false;
}
if (bFound == false)
{
// update the agent.map file with the new knowledge
try
{
System.IO.StreamWriter sw = new System.IO.StreamWriter(System.IO.Path.Combine(basePath,DefaultPersistenceModule.AgentMapFile),true);
sw.WriteLine(knowledgePersistent.Name);
sw.Close();
}
catch(System.IO.IOException ioex)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Error persisting agent data", ioex);
return false;
}
}
return true;
}
else
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, String.Format("No agent with name {0} has been found", aid.GlobalName));
return false;
}
}
///
/// Resumes the persisted data of a given agent.
///
/// agent identifier
/// agent persisted data
/// true if successful
public bool ResumeAgent(AID aid, ref AgentService.Agent.AgentPersistent agent)
{
System.Runtime.Serialization.IFormatter formatter;
System.IO.StreamReader sr = null;
Stream stream = null;
object value = this.mappings[aid.GlobalName];
if (value == null)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not find agent " + aid.GlobalName);
return false;
}
string basePath = System.IO.Path.Combine(this.persistenceRoot,(value as string));
try
{
formatter = new BinaryFormatter();
formatter.Binder = new ASBinder();
string agentFile = System.IO.Path.Combine(basePath,DefaultPersistenceModule.AgentDataFile);
stream = new FileStream(agentFile, FileMode.Open, FileAccess.Read, FileShare.Read);
agent = formatter.Deserialize(stream) as Agent.AgentPersistent;
stream.Close();
sr = new System.IO.StreamReader(System.IO.Path.Combine(basePath,DefaultPersistenceModule.AgentMapFile));
agent.PersistedKnowledges.RemoveAll();
string read = "";
while (((read = sr.ReadLine()) != "") && (read != null))
{
string kpfile = System.IO.Path.Combine(basePath, read + DefaultPersistenceModule.SerializationExt);
formatter = new BinaryFormatter();
formatter.Binder = new ASBinder();
stream = new FileStream(kpfile, FileMode.Open, FileAccess.Read, FileShare.Read);
KnowledgePersistent kp = (KnowledgePersistent) formatter.Deserialize(stream);
stream.Close();
agent.PersistedKnowledges.Add(kp);
}
return true;
}
catch(System.IO.IOException ioex)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not access agent persistent data", ioex);
return false;
}
catch(System.Exception ex)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "unexpected error while trying to resume " + aid.GlobalName, ex);
return false;
}
finally
{
if(stream != null)
{
stream.Close();
}
if (sr != null)
{
sr.Close();
}
}
}
#endregion
#region Worker Methods
///
/// Persists the knowledge object to the file system
///
/// persistent version of the knowledge object
/// file where the knowledge is persisted
/// true if the process does not encounter errors
private bool doPersistKnowledge(KnowledgePersistent kp, string file)
{
System.IO.FileStream fs = new System.IO.FileStream(file, FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Binder = new ASBinder();
try
{
formatter.Serialize(fs, kp);
}
catch (System.Runtime.Serialization.SerializationException e)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not write to serialization file", e);
return false;
}
catch(System.IO.IOException ioex)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Cannot persist knowledge " + kp.Name, ioex);
return false;
}
finally
{
fs.Close();
}
return true;
}
///
/// Serializes the mapping hashtable
///
/// true if the serialization process has been successful
private bool doSerializeMappings()
{
System.IO.FileStream fs = new System.IO.FileStream(System.IO.Path.Combine(this.persistenceRoot,this.mapFile), FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Binder = new ASBinder();
try
{
formatter.Serialize(fs, this.mappings);
return true;
}
catch (System.Runtime.Serialization.SerializationException e)
{
this.lastError = new ModuleError((int)ModuleErrorCodes.InternalError, this.info, "Could not write to serialization file", e);
return false;
}
finally
{
fs.Close();
}
}
///
/// reads the data from the configuration file
///
/// path to the configuration file
private void readData(string configFile)
{
System.IO.StreamReader reader = new System.IO.StreamReader(configFile);
this.persistenceRoot = reader.ReadLine();
if (this.persistenceRoot.EndsWith("" + System.IO.Path.DirectorySeparatorChar) == false)
this.persistenceRoot += System.IO.Path.DirectorySeparatorChar;
this.mapFile = reader.ReadLine();
reader.Close();
}
#endregion
}
}