/*********************************************************************/ /* */ /* 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.Reflection; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using AgentService.Interop; using AgentService.Agent; using AgentService.Platform.Configuration; using AgentService.Platform.Modules; using AgentService.Platform.Commands; using AgentService.WCFServer; namespace AgentService.Platform { /// /// Class PlatformController. Manages the life cycle of the platform /// [Serializable] public class PlatformController : IPlatformController { private ASWCFServer wcfserver = null; public ASWCFServer WCFServer { get { return this.wcfserver; } } /// /// Gets access to the platform console /// sub system. /// public IConsole Console { get { return this.console; } } /// /// Module management. /// private ModuleController modules; /// /// Gets the module controller reference. /// public IModuleController Modules { get { return this.modules; } } /// /// Services management. /// private ServiceController services; /// /// Gets the service controller reference. /// public IServiceController Services { get { return this.services; } } #region Remoting Service Member /// /// Remoting channel /// private TcpChannel platformChannel; #endregion #region Platform Related Members /// /// Reference to the agent platform to be controlled /// private Platform platform; /// /// Gets the platform state if the /// Platform has been bound otherwise /// it returns the PlatformState.Unknown /// state /// public PlatformState PlatformState { get { if (this.platform != null) { return this.platform.State; } return PlatformState.Unknown; } } /// /// Gets the platform description, null if the /// platform has not been bound /// public IPlatformDescription PlatformDescription { get { if (this.platform == null) return null; return this.platform.Description; } } /// /// Flag that control the platform binding process. /// If false the platform is not bounded. /// private bool bBound = false; #endregion #region Commands and Threads /// /// AgentService system command queue, every command issued to the /// platform is routed to this queue /// private CommandQueue cmdQueue; /// /// thread associated with the shell object /// private System.Threading.Thread shellThread; /// /// Thread that dispatches and executes commands /// retrieved from the queue /// private System.Threading.Thread cmdThread; /// /// Context used by the commands to performs /// their task /// private ICommandContext cmdContext = null; #endregion #region Console and Shell /// /// Console Writer object: handles main and error console. /// private PlatformConsole console; /// /// Security token given by the platform /// to control the platform's state /// protected int platformToken; /// /// path manager object used to retrieve files /// private FileSystem fileSystem; /// /// Platformshell object: abstracts the platform shell. /// private CommandParser cmdParser; /// /// Flag, if true the AgentService platform has a shell active /// private bool bRunShell = false; #endregion #region Constructors /// /// Constructs a PlatformController object, and attaches to it a writer and a path manager /// /// reference to the PlatformConsole object used to display platform messages /// reference to the FileSystem object used to give information about the path public PlatformController(PlatformConsole console, FileSystem pathMan) { this.console = console; this.fileSystem = pathMan; } /// /// Constructs a PlatformController object, and attaches to it the default PlatformConsole and a path manager /// /// reference to the FileSystem object used to give information about the path public PlatformController(FileSystem pathMan) { this.console = new PlatformConsole(System.Console.Out, System.Console.Error, System.Console.In); this.fileSystem = pathMan; } /// /// Constructs a PlatformController object, and attaches to it a writer and a path manager /// /// reference to the TextWriter object used to write information messages /// reference to the TextWriter object used to write error messages /// reference to the TextReader object used to read input /// reference to the FileSystem object used to give information about the path public PlatformController(System.IO.TextWriter mainConsole, System.IO.TextWriter errorConsole, TextReader inputConsole, FileSystem pathMan) { this.console = new PlatformConsole(mainConsole, errorConsole, inputConsole); this.fileSystem = pathMan; } /// /// Constructs a PlatformController object, and attaches to it a writer and a path manager /// /// reference to the TextWriter object used to write information messages /// reference to the TextWriter object used to write error messages /// reference to the TextWriter object used to write warning messages /// reference to the TextReader object used as standard input /// reference to the FileSystem object used to give information about the path public PlatformController(TextWriter mainConsole, TextWriter errorConsole, TextWriter warningConsole, TextReader inputConsole, FileSystem pathMan) { this.console = new PlatformConsole(mainConsole, errorConsole, warningConsole, mainConsole, inputConsole); this.fileSystem = pathMan; } #endregion #region Platform Commands /// /// Install an instance of the AgentService platfom. /// /// installation file /// factory object for creting manager of configuration and installation files public bool InstallPlatform(string installFile, ManagerFactory managerFactory) { this.console.WriteLineMessage("Installing AgentService platform.."); InstallationData inst = null; this.console.WriteMessage(" [Install] - 1 - Initializing the installer..."); InstallManager manager = managerFactory.CreateInstallManager(installFile); this.console.AppendMessage(String.Format("..{0} [OK]",managerFactory.Format)); this.console.WriteMessage(" [Install] - 2 - Reading the installation file..."); inst = manager.Read(); if (inst != null) { if (inst.HasErrors == false) { this.console.AppendMessage("..[OK]"); try { string path = inst.BaseDirPath; if (inst.BaseDirPath.EndsWith("" + System.IO.Path.DirectorySeparatorChar) == false) { path += System.IO.Path.DirectorySeparatorChar; } // first create directory tree this.console.WriteLineMessage(" [Install] - 3 - Creating directory structure..."); // base dir; this.console.WriteLineMessage(" - base directory : " + path); this.fileSystem.CreatePlatformBaseDirectory(path); // modules subdir this.console.WriteLineMessage(" - modules sub-directories"); this.fileSystem.CreateModulesDirectories(); // agents this.console.WriteLineMessage(" - agents sub-directory"); this.fileSystem.CreateAgentsDirectory(); // conf dir this.console.WriteLineMessage(" - configuration sub-directory"); this.fileSystem.CreateConfigurationDirectory(); // ok now copying files this.console.WriteLineMessage(" [Install] - 4 - Copying files.."); this.console.WriteLineMessage(" - AgentService interface library : AgentServiceInterface.dll"); this.fileSystem.CopyAgentServiceCoreFile("AgentServiceInterface.dll"); this.console.WriteLineMessage(" - AgentService library : AgentService.dll"); this.fileSystem.CopyAgentServiceCoreFile("AgentService.dll"); this.console.WriteLineMessage(" - xml support (runs in Mono and .NET only): xmlconf.dll"); this.fileSystem.CopyAgentServiceCoreFile("xmlconf.dll"); this.console.WriteLineMessage(" - utilities : utilities.dll"); this.fileSystem.CopyAgentServiceCoreFile("utilities.dll"); this.console.WriteLineMessage(" - CompactFormatterPlus : CompactFormatterPlus.dll"); this.fileSystem.CopyAgentServiceCoreFile("CompactFormatterPlus.dll"); this.console.WriteLineMessage(" - ADOHelper : ADOHelper.dll"); this.fileSystem.CopyAgentServiceCoreFile("ADOHelper.dll"); this.console.WriteLineMessage(" - ISerial : ISerial.dll"); this.fileSystem.CopyAgentServiceCoreFile("ISerial.dll"); this.console.WriteLineMessage(" - AgentService Platform starter : asdrv.exe"); this.fileSystem.CopyAgentServiceCoreFile("asdrv.exe"); this.console.WriteLineMessage(" [Install] - 5 - Copying DLLs core modules.."); this.console.WriteMessage(" - Storage module ..."); this.fileSystem.CopyModule(inst.Storage.AssemblyName,null,true); this.console.AppendMessage(".. [OK]"); this.console.WriteMessage(" - Persistence module ..."); this.fileSystem.CopyModule(inst.Persistence.AssemblyName, null, true); this.console.AppendMessage(".. [OK]"); this.console.WriteMessage(" - Messaging module ..."); this.fileSystem.CopyModule(inst.Messaging.AssemblyName, null, true); this.console.AppendMessage(".. [OK]"); this.console.WriteMessage(" - Logging module ..."); this.fileSystem.CopyModule(inst.Logging.AssemblyName, null, true); this.console.AppendMessage(".. [OK]"); this.console.WriteLineMessage(" [Install] - 6 - Copying DLLs for additional modules.."); if (inst.AdditionalModules.Length > 0) { foreach (ModuleInfo mi in inst.AdditionalModules) { this.console.WriteMessage(String.Format(" - Module {0} ...", mi.FullTypeName)); this.fileSystem.CopyModule(mi.AssemblyName, null, false); this.console.AppendMessage(".. [OK]"); } } else { this.console.WriteLineMessage(" no additional modules selected for installation"); } this.console.WriteMessage(" [Install] - 7 - Creating default configuration file.."); // Create the new path for the configueration file string confPath = System.IO.Path.Combine(inst.BaseDirPath,Platform.ConfDir); // write default configuration file manager.ConfigurationFile = this.fileSystem.DefaultConfPath; manager.GenerateDefaultConfigurationFile(inst); if (manager.InfoMessage != null) { this.console.AppendMessage(".. [NO]"); this.console.WriteError("[ERROR] - unable to generate the configuration file : " + manager.InfoMessage); this.console.WriteLineMessage("Installation with errors"); } else { this.console.AppendMessage(".. [OK]"); this.console.WriteLineMessage("Installation Successful"); } return true; } catch(System.IO.IOException ioex) { this.console.WriteError("[ERROR] io exception : " + ioex.Message); } } else { this.console.AppendMessage(".. [NO]"); //MOD: 02/05/2006 : write the error found in installation file this.console.WriteError("[ERROR] - installation file has errors: " + inst.ErrorMessage); } } else { this.console.WriteLineMessage(".. [ERROR]"); // moves to __ROLLBACK } __ROLLBACK: this.console.WriteMessage("Rolling back installation..."); // TODO: execute rollback if (inst != null) { try { this.fileSystem.DeletePlatformTree(); } catch (IOException ioex) { this.console.WriteError("[ERROR] - Could not completely roll back installation [ex:{0}]", ioex.Message); } } this.console.AppendMessage(" .. [OK]"); this.console.WriteLineMessage("Installation failed"); return false; } /// /// Uninstall an instance of the AgentService platform. /// /// installation file /// factory object for configuration /// and installation files managers public bool Uninstall(string installFile, ManagerFactory managerFactory) { InstallManager manager = null; this.console.WriteLineMessage("Uninstalling the AgentService platform..."); this.console.WriteMessage(" [Uninstall] - 1 - Initializing the installer... "); manager = managerFactory.CreateInstallManager(installFile); this.console.AppendMessage(String.Format(".. {0} [OK]", managerFactory.Format)); this.console.WriteMessage(" [Uninstall] - 2 - Reading the installation file..."); InstallationData inst = manager.Read(); if (inst != null) { this.console.AppendMessage(".. [OK]"); this.console.WriteMessage(" [Uninstall] - 3 - Checking and stopping the platform..."); // TODO: Platform.Uninstall check and stop the platform this.console.AppendMessage(".. [OK]"); this.console.WriteLineMessage(" [Uninstall] - 4 - Uninstalling modules.."); this.console.WriteMessage(" - uninstalling additional modules..."); // TODO: Platform.Uninstall check and stop the platform this.console.AppendMessage(".. [OK]"); this.console.WriteLineMessage(" - uninstalling core modules..."); this.console.WriteMessage(" - logging..."); this.console.AppendMessage(".. [OK]"); this.console.WriteMessage(" - messaging..."); this.console.AppendMessage(".. [OK]"); this.console.WriteMessage(" - persistence..."); this.console.AppendMessage(".. [OK]"); this.console.WriteMessage(" - storage..."); this.console.AppendMessage(".. [OK]"); // TODO: Platform.Uninstall check and stop the platform this.console.WriteLineMessage("Uninstall successful"); this.console.WriteLineMessage(String.Format(" Remove the {0} directory ad all the contained folders.",inst.BaseDirPath)); return true; } else { this.console.AppendMessage(".. [ERROR]"); this.console.WriteError("unable to read configuration file!"); this.console.WriteLineMessage("Uninstall aborted"); } __FAILED: return false; } /// /// Binds the controller object to the platform defined /// by the given configuration file /// /// path to the platform configuration file /// factory object for creting manager of configuration and installation files public void BindToPlatform(string configFile, ManagerFactory managerFactory) { if (this.bBound == false) { this.platform = new Platform(configFile, managerFactory, this.console, this.fileSystem, out this.platformToken); this.modules = new ModuleController(this.platform); this.services = new ServiceController(this.platform); this.cmdQueue = new LinkedCommandQueue(); // new CircularCommandQueue(); this.cmdThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.routeCommands)); this.bBound = true; } else { // error with the console writer } } /// /// Detaches the controller from the Platform and shutdown the /// platform /// public void UnBind() { if (this.bBound == true) { // TODO: implement this method this.bRunShell = false; this.bBound = false; } } /// /// Starts the AgentService platform, and puts it in the ready state /// public void StartPlatform() { if (this.platform.State != PlatformState.Idle) { this.console.WriteError("Invalid call! Platform is not in Idle state."); return; } this.platform.SetState(PlatformState.Starting, this.platformToken); this.console.WriteLineMessage("Platform Starts... "); AppDomain.CurrentDomain.AppendPrivatePath(this.platform.Configuration.CoreModulesBinPath); AppDomain.CurrentDomain.AppendPrivatePath(this.platform.Configuration.AdditionalModulesBinPath); // step two: loading core modules this.console.WriteLineMessage(" [Starting] - 1 - Loading Core Modules... "); bool bOk = this.platform.LoadCoreModules(); if (bOk == false) { goto __ROLLBACK; } // step three: loading additional modules this.console.WriteLineMessage(" [Starting] - 2 - Loading Additional Modules... "); bOk = this.platform.LoadAdditionalModules(); if (bOk == false) { if (this.platform.Configuration.SkipFailedModules == false) goto __ROLLBACK; } this.platform.SetState(PlatformState.Ready, this.platformToken); this.console.WriteLineMessage("Platform ready."); return; __ROLLBACK: this.RollBack(PlatformState.Idle); } /// /// Makes the platform running, by acivating first the core agents and /// then the user agents /// public void RunPlatform() { if (this.platform.State == PlatformState.Ready) { this.platform.SetState(PlatformState.Activating, this.platformToken); this.console.WriteLineMessage("Platform activating..."); //MOD : 18/05/2006 : added activation of selected Factory and Policy this.console.WriteLineMessage(" [Activating] - 1 - Activating Factory... "); bool bok = this.platform.ActivateFactory(); if (bok == false) goto __ROLLBACK; // step four: activating core agents this.console.WriteLineMessage(" [Activating] - 2 - Activating Core Agents... "); bool bOk = this.platform.ActivateCoreAgents(); if (bOk == false) goto __ROLLBACK; // step five: activating WCF server (if requested) bool b = bool.Parse(this.platform.Configuration.Properties["System.WcfServer.Enable"]); if (b == true) { string ip = this.platform.Configuration.Properties["System.WcfServer.IP"]; string port = this.platform.Configuration.Properties["System.WcfServer.TCPPort"]; string address = "http://" + ip + ":" + port + "/"; this.console.WriteLineMessage(" [Activating] - 3 - Starting WCF Server... "); wcfserver = new ASWCFServer(new Uri(address), this.platform); wcfserver.startServer(); } string numActive; if (b == true) numActive = "4"; else numActive = "3"; // step six: activating user agents this.console.WriteLineMessage(" [Activating] - "+numActive+" - Activating User Agents... "); bOk = this.platform.ActivateUserAgents(); if (bOk == false) goto __ROLLBACK; this.platform.SetState(PlatformState.Running, this.platformToken); this.console.WriteLineMessage("Platform Running"); return; __ROLLBACK: this.RollBack(PlatformState.Ready); } else this.console.WriteError("Invalid Call, Cannot Run the platform. Platform is not in the ready state."); } /// /// Stops the platform and puts it into the ready state /// public void StopPlatform() { if (this.platform.State == PlatformState.Running) { this.console.WriteLineMessage("Platform is stopping.."); this.platform.SetState(PlatformState.Stopping, this.platformToken); this.console.WriteLineMessage(" [Stopping] - 2 - Stopping and Persisting User Agents..."); bool bOk = this.platform.StopAndPersistUserAgents(); this.console.WriteLineMessage(" [Stopping] - 1 - Stopping and Persisting Core Agents..."); bOk = this.platform.StopAndPersistCoreAgents(); this.platform.SetState(PlatformState.Ready, this.platformToken); this.console.WriteLineMessage("Platform Stopped [ready mode]."); } else this.console.WriteError("Invalid Call, Cannot Stop the platform. Platform is not running."); } /// /// Restarts the AgentService platform. Restart implies the shutdown and start. /// public void RestartPlatform() { if ((this.platform.State == PlatformState.Running) || (this.platform.State == PlatformState.Ready)) { this.ShutdownPlatform(); } if (this.platform.State == PlatformState.Idle) { this.StartPlatform(); } else { this.console.WriteLineMessage("Invalid call. Cannot Restart platform."); } } /// /// Shuts down the AgentService platform. /// public void ShutdownPlatform() { // TODO: What about the Idle state? if (this.platform.State == PlatformState.FailStart) { // TODO: deal shutting down when something has // gone wrong } if (this.platform.State == PlatformState.Running) { this.StopPlatform(); } if (this.platform.State == PlatformState.Ready) { bool bOk = false; this.platform.SetState(PlatformState.ShuttingDown, this.platformToken); this.console.WriteLineMessage("Platform is Shutting Down... "); if(wcfserver!=null) { this.console.WriteLineMessage(" [Shutting] - 4 - Stopping WCFServer..."); this.wcfserver.stopServer(); } this.console.WriteLineMessage(" [Shutting] - 3 - Unloading Additional Modules..."); bOk = this.platform.UnloadAdditionalModules(); this.console.WriteLineMessage(" [Shutting] - 2 - Unloading Core Modules..."); bOk = this.platform.UnloadCoreModules(); this.console.WriteMessage(" [Shutting] - 1 - Writing to Configuration File..."); // MOD: Christian Vecchiola - 12/10/2006 // // Moved IsFirstStart update here // this.platform.Configuration.BeginUpdate(); this.platform.Configuration.IsFirstStart = false; this.platform.Configuration.EndUpdate(); // // END MOD if (this.platform.WriteToConfigurationFile() == true) { this.console.AppendMessage(".. [OK]"); } else this.console.AppendMessage(".. [ERROR]"); this.console.WriteLineMessage("Platform shut down."); this.platform.SetState(PlatformState.Idle, this.platformToken); } else { this.console.WriteError("Invalid call!, Platform is not in the ready state"); } } /// /// Rolls back the platform to the given state /// private void RollBack(PlatformState backState) { this.console.WriteLineMessage("Rolling Back action..."); if (backState != this.platform.State) { // TODO: Add code to handle the rolling back action. } this.platform.SetState(backState, this.platformToken); this.console.WriteLineMessage("Rollback done."); } /// /// Attaches a shell to the platform. /// /// platform shell, if null the current shell is detached from the platform public void BindToInput(TextReader commandConsole) { // first check the previous shell if (this.cmdParser != null) { // first stop the previous shell this.shellThread.Abort(); } // then check the new shell if (commandConsole != null) { this.cmdParser = new CommandParser(commandConsole); if (this.shellThread == null) { this.bRunShell = true; this.shellThread = new System.Threading.Thread(new System.Threading.ThreadStart(this.runShell)); } this.shellThread.Start(); } } /// /// Sets the output console for the AgentService platform. /// /// Reference to the TextWriter object used as error console. /// Reference to the TextWriter object used as main console. /// Reference to the TextWriter object used by agents as console. public void BindToOutputs(TextWriter mainConsole, TextWriter errorConsole, TextWriter platformConsole) { // will be sufficient? this.console.Error = errorConsole; this.console.Main = mainConsole; this.console.BlackBoard = new DefaultPlatformConsole(platformConsole); } /// /// Sets the output console for the AgentService platform. /// /// reference to the TextWriter object used as error console /// reference to the TextWriter object used as main console /// reference to the TextWriter object used as warning console /// reference to the TextWriter object used by agents as console public void BindToOutputs(TextWriter mainConsole, TextWriter errorConsole, TextWriter warningConsole, TextWriter platformConsole) { // will be sufficient? this.console.Error = errorConsole; this.console.Main = mainConsole; this.console.Warning = warningConsole; this.console.BlackBoard = new DefaultPlatformConsole(platformConsole); } /// /// Sets up the shell and waits for commands. /// private void runShell() { while(this.bRunShell) { // get the next command, PlatformCommand cmd = this.cmdParser.NextCommand(); // execute post commmand this.PostCommand(cmd); } } #endregion #region Command Execution /// /// Retrieves commands from the queue and perform their execution /// private void routeCommands() { while(this.bBound==true) { while(this.cmdQueue.IsEmpty == false) { PlatformCommand cmd = null; lock(this.cmdQueue) { cmd = this.cmdQueue.DeQueue(); } // checks if the platform is in a state that // allows the execution of the current commmand bool bCanExecute = this.checkCommand(cmd); if(bCanExecute == true) { cmd.Execute(this); } else { // TODO: ma un nome per il comando serve? Cosi so cosa sto sbagliando. //this.writer.WriteError("Cannot execute command {0}: platform state [{1}] command level [{2}]", // cmd.Type, this.PlatformState, cmd.State); } } this.cmdThread.Suspend(); } } /// /// Checks if the platform is in a state that /// allows the execution of the current commmand /// /// Evaluated Command /// True if the command can be execute private bool checkCommand(PlatformCommand cmd) { // first check if the platform is in transient state if ((this.PlatformState & PlatformState.TransientMask) > 0) { return false; } // now check the state of the command and see if // the state of the command is transient if ((cmd.State & PlatformState.TransientMask) > 0) { return false; } // does the command requires the exact matching? if (cmd.MatchExact == true) { // ..yes return (cmd.State == this.PlatformState); } //..no return cmd.State <= this.PlatformState; } /// /// Posts a command into the CommandQueue /// /// command that will be executed public void PostCommand(PlatformCommand cmd) { if (this.PlatformState > PlatformState.Idle) { if (this.cmdQueue != null) { lock(this.cmdQueue) { // enqueue the command this.cmdQueue.EnQueue(cmd); } // resume the routing command thread if suspended if (this.cmdThread.ThreadState == System.Threading.ThreadState.Suspended) this.cmdThread.Resume(); } } else { // TODO: LOG ERROR } } #endregion #region Remoting Services /// /// Starts the remoting service by opening a tcp remoting channel /// /// port number /// if true secures the channel /// null if operation succeds, exception object otherwise public System.Exception StartRemotingService(int port, bool bSecureChannel) { if ((1024 < port) && (port < 65536)) { try { /* // Create channel this.platformChannel = new TcpChannel(port); ChannelServices.RegisterChannel(this.platformChannel,bSecureChannel); //Register type RemotingConfiguration.RegisterWellKnownServiceType( typeof(RemoteASManager), this.PlatformDescription.Name, WellKnownObjectMode.Singleton); // Open channel this.manager = (RemoteASManager)Activator.GetObject( typeof(RemoteASManager), "tcp://localhost:"+port+"/"+this.PlatformDescription.Name); AgentServiceManager asm = new AgentServiceManager(); asm.Controller = this; this.manager.SetManager(asm); return null; */ return new NotImplementedException("Remoting Service is not implemented"); } catch(System.Exception ex) { if (this.platformChannel != null) { this.StopRemotingService(); } return ex; } } return new System.Exception(String.Format("Port {0} is not valid. Must be between 1024 and 65536", port)); } /// /// Stops the rtemoting service /// /// null if operation succeds, exception object otherwise public System.Exception StopRemotingService() { try { //Close Channel ChannelServices.UnregisterChannel(this.platformChannel); return null; } catch(System.Runtime.Remoting.RemotingException rex) { return rex; } catch(System.Security.SecurityException sex) { return sex; } } #endregion } }