Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using System.Diagnostics;
- using System.IO;
- using System.Threading;
- using System.Runtime.CompilerServices;
- using IPC;
- using EngineBrowser.IPC.Protocol;
- using Newtonsoft.Json;
- using ExSearch.EngineCefSharp.Service.Abstract.Managers;
- using ExSearch.Engine.Core.Abstract.General;
- using ExSearch.EngineCefSharp.Service.Utils;
- using ExSearch.Engine.Core.Abstract.Log;
- using ExSearch.Engine.Core.Utils;
- using ExSearch.EngineCefSharp.Service.Entities.Managers;
- namespace ExSearch.EngineCefSharp.Service.Concrete.Managers
- {
- /// <summary>
- /// Realization IBrowserManager, IBrowser.
- ///Allows to manage ExSearch.Engine.BrowserCefSharp.exe processes - run, stop.
- /// </summary>
- internal sealed class BrowserManager : IDisposable, IBrowserManager, IBrowser
- {
- #region FIELDS
- private readonly ILog log;
- private readonly string browserPath;
- private readonly int browserInstancesCount;
- private readonly ThreadSafeList<BrowserLogic> browsers;
- private SemaphoreSlim browserThreadsLimit;
- private ManualResetEventSlim allBrowsersLaunched;
- #endregion
- #region CONSTRUCTORS
- public BrowserManager(ILog log)
- {
- this.log = log;
- this.browserPath = Configurator.BrowserPath;
- this.browserInstancesCount = Configurator.BrowserInstancesCount;
- this.browsers = new ThreadSafeList<BrowserLogic>(this.browserInstancesCount);
- this.browserThreadsLimit = new SemaphoreSlim(this.browserInstancesCount, this.browserInstancesCount);
- this.allBrowsersLaunched = new ManualResetEventSlim(false);
- InitBrowsers();
- }
- #endregion
- #region PROPERTIES
- #endregion
- #region METHODS
- #region IBrowserManager
- /// <summary>
- /// Run all browsers.
- /// </summary>
- public void RunBrowsers()
- {
- // kill old/mistaking browsers
- KillBrowsers();
- BrowserLogic[] browserLogicArray = this.browsers.ToArray();
- foreach (BrowserLogic browserLogic in browserLogicArray)
- browserLogic.Run();
- // checking async all browsers
- Task.Factory.StartNew(() =>
- {
- while (true)
- {
- Thread.Sleep(100);
- if (browserInstancesCount.Equals(browserLogicArray.Count(browserLogicElem => browserLogicElem.State != BrowserState.Unavailable)))
- {
- try
- {
- if (this.allBrowsersLaunched != null)
- this.allBrowsersLaunched.Set();
- }
- catch { }
- break;
- }
- }
- });
- }
- /// <summary>
- /// Stop all browsers.
- /// </summary>
- public void ExitBrowsers()
- {
- if (this.allBrowsersLaunched != null)
- this.allBrowsersLaunched.Reset();
- BrowserLogic[] browserLogicArray = this.browsers.ToArray();
- foreach (BrowserLogic browserLogic in browserLogicArray)
- browserLogic.Exit();
- // delete old/mistaking browsers
- KillBrowsers();
- }
- #endregion
- #region IBrowser
- /// <summary>
- /// Load content (html page only).
- /// </summary>
- public Content Download(string url, bool fullContent)
- {
- if (string.IsNullOrWhiteSpace(url))
- throw new ArgumentException("URL not specified or empty");
- allBrowsersLaunched.Wait(); // for initialize all browsers
- browserThreadsLimit.Wait();
- try
- {
- BrowserLogic browserLogic = AcquireBrowser();
- try
- {
- return browserLogic.Download(url, fullContent);
- }
- finally
- {
- ReleaseBrowser(browserLogic);
- }
- }
- finally
- {
- browserThreadsLimit.Release();
- }
- }
- #endregion
- #region IDisposable
- public void Dispose()
- {
- DeinitBrowsers();
- if (browserThreadsLimit != null)
- {
- browserThreadsLimit.Dispose();
- browserThreadsLimit = null;
- }
- if (allBrowsersLaunched != null)
- {
- allBrowsersLaunched.Dispose();
- allBrowsersLaunched = null;
- }
- }
- #endregion
- #region Private
- /// <summary>
- /// Request a free browser and make it used.
- /// </summary>
- [MethodImpl(MethodImplOptions.Synchronized)]
- private BrowserLogic AcquireBrowser()
- {
- BrowserLogic browserLogic;
- while (true)
- {
- browserLogic = browsers.ToArray().FirstOrDefault(browserLogicElem =>
- {
- if (browserLogicElem.State == BrowserState.Launched)
- {
- browserLogicElem.State = BrowserState.InProcess;
- return true;
- }
- return false;
- });
- if (browserLogic != null)
- break;
- else
- Thread.Sleep(100);
- }
- return browserLogic;
- }
- /// <summary>
- /// Free a browser being in use.
- /// </summary>
- private void ReleaseBrowser(BrowserLogic browserLogic)
- {
- if (browserLogic.State == BrowserState.InProcess)
- browserLogic.State = BrowserState.Launched;
- }
- private void InitBrowsers()
- {
- for (int i = 0; i < browserInstancesCount; i++)
- browsers.Add(new BrowserLogic(this.browserPath, i + 1, Guid.NewGuid()));
- }
- private void DeinitBrowsers()
- {
- for (int i = 0; i < browserInstancesCount; i++)
- if (browsers[i] != null)
- {
- browsers[i].Dispose();
- browsers[i] = null;
- }
- browsers.Clear();
- }
- /// <summary>
- /// Delete all browsers.
- /// </summary>
- private void KillBrowsers()
- {
- Process[] browsers = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(this.browserPath));
- foreach (Process browser in browsers)
- try
- {
- browser.Kill();
- }
- catch { }
- }
- #endregion
- #endregion
- #region NESTED TYPES (ENUMS, STRUCTURES, CLASSES)
- /// <summary>
- /// Class describing one browser instance functionality.
- /// </summary>
- private sealed class BrowserLogic : IDisposable
- {
- #region FIELDS
- private readonly string browserPath;
- private Process process;
- private IPCSender<string, string> ipcSender;
- private readonly Synchronized<BrowserState> state;
- private readonly int number;
- private readonly Guid guid;
- private ManualResetEventSlim whileInProcessState;
- #endregion
- #region CONSTRUCTORS
- public BrowserLogic(string browserPath, int number, Guid guid)
- {
- this.browserPath = browserPath;
- this.state = new Synchronized<BrowserState>(BrowserState.Unavailable);
- this.number = number;
- this.guid = guid;
- this.whileInProcessState = new ManualResetEventSlim(true);
- }
- #endregion
- #region PROPERTIES
- /// <summary>
- /// A browser state.
- /// </summary>
- public BrowserState State
- {
- get
- {
- return this.state.Value;
- }
- set
- {
- if (whileInProcessState != null && value == BrowserState.InProcess)
- whileInProcessState.Reset();
- this.state.Value = value;
- if (whileInProcessState != null && value == BrowserState.Launched)
- whileInProcessState.Set();
- }
- }
- #endregion
- #region METHODS
- #region IDisposable
- public void Dispose()
- {
- if (this.process != null)
- {
- this.process.Dispose();
- this.process = null;
- }
- if (this.whileInProcessState != null)
- {
- this.whileInProcessState.Dispose();
- this.whileInProcessState = null;
- }
- }
- #endregion
- public void Run()
- {
- RunProcess();
- StartIPCSender();
- Task.Factory.StartNew(() =>
- {
- bool availabilityProcess = false;
- do
- {
- Thread.Sleep(100);
- string returnString = ipcSender.Send(JsonConvert.SerializeObject(new Message { Command = Command.Ping }));
- if (returnString != null)
- {
- Return returnObj = JsonConvert.DeserializeObject<Return>(returnString);
- if (returnObj.Command == Command.Ping)
- availabilityProcess = true;
- }
- }
- while (!availabilityProcess);
- this.State = BrowserState.Launched;
- });
- }
- public void Exit()
- {
- bool canExit = false;
- if (this.State == BrowserState.Launched)
- {
- this.State = BrowserState.Unavailable;
- canExit = true;
- }
- else if (this.State == BrowserState.InProcess)
- {
- if (whileInProcessState != null)
- whileInProcessState.Wait();
- this.State = BrowserState.Unavailable;
- canExit = true;
- }
- if (canExit)
- {
- string returnString = ipcSender.Send(JsonConvert.SerializeObject(new Message { Command = Command.Exit }));
- if (returnString != null)
- {
- Return returnObj = JsonConvert.DeserializeObject<Return>(returnString);
- if (returnObj.Command == Command.Exit)
- {
- StopIPCSender();
- ExitProcess();
- }
- }
- }
- }
- public Content Download(string url, bool fullContent)
- {
- string returnString = ipcSender.Send(JsonConvert.SerializeObject(new Message { Command = fullContent ? Command.DownloadFull : Command.Download, ContentType = ContentType.Data, Content = JsonConvert.SerializeObject(fullContent ? (object)(new DownloadFullMessageContent { Url = url }) : (object)(new DownloadMessageContent { Url = url })) }));
- if (returnString == null) // connection error
- throw new Exception("Can't retrieve content. Connection error");
- Return returnObj = JsonConvert.DeserializeObject<Return>(returnString);
- if (returnObj.ContentType == ContentType.Data)
- {
- switch (returnObj.Command)
- {
- case Command.DownloadFull:
- DownloadFullReturnContent downloadFullReturnContent = JsonConvert.DeserializeObject<DownloadFullReturnContent>(returnObj.Content);
- return null; // TODO!!!
- case Command.Download:
- DownloadReturnContent downloadReturnContent = JsonConvert.DeserializeObject<DownloadReturnContent>(returnObj.Content);
- return new Content { IsArchive = false, ContentUrl = url, FileName = downloadReturnContent.FileName, ContentType = downloadReturnContent.MimeType, ContentData = downloadReturnContent.Data };
- default:
- throw new Exception(string.Format("Can't retrieve content. Protocol (Command.{0}) error.", Enum.GetName(returnObj.Command.GetType(), returnObj.Command)));
- }
- }
- else
- throw new Exception(string.Format("Can't retrieve content. {0}", returnObj.ContentType == ContentType.Error ? returnObj.Content : "Protocol error (ContentType.Empty)."));
- }
- private void RunProcess()
- {
- ProcessStartInfo processStartInfo = new ProcessStartInfo();
- processStartInfo.FileName = this.browserPath;
- processStartInfo.Arguments = string.Format(" /number {0} /guid {1}", this.number, this.guid.ToString("N"));
- processStartInfo.LoadUserProfile = true;
- processStartInfo.UseShellExecute = false;
- processStartInfo.WorkingDirectory = Path.GetDirectoryName(processStartInfo.FileName);
- this.process = Process.Start(processStartInfo);
- }
- private void ExitProcess()
- {
- if (this.process != null)
- {
- this.process.WaitForExit();
- this.process.Dispose();
- this.process = null;
- }
- }
- private void StartIPCSender()
- {
- StopIPCSender();
- this.ipcSender = new IPCSender<string, string>();
- this.ipcSender.Open(new Channel(guid.ToString("N")));
- }
- private void StopIPCSender()
- {
- if (ipcSender != null)
- ipcSender = null;
- }
- #endregion
- #region NESTED TYPES (ENUMS, STRUCTURES, CLASSES)
- #endregion
- }
- #endregion
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement