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