PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/source/library/Interlace/ReactorService/ServiceHost.cs

https://bitbucket.org/VahidN/interlace
C# | 538 lines | 395 code | 101 blank | 42 comment | 50 complexity | bf9ad0e50d5022da38513bf6b24abd2e MD5 | raw file
  1. #region Using Directives and Copyright Notice
  2. // Copyright (c) 2007-2010, Computer Consultancy Pty Ltd
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. // * Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above copyright
  10. // notice, this list of conditions and the following disclaimer in the
  11. // documentation and/or other materials provided with the distribution.
  12. // * Neither the name of the Computer Consultancy Pty Ltd nor the
  13. // names of its contributors may be used to endorse or promote products
  14. // derived from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. // ARE DISCLAIMED. IN NO EVENT SHALL COMPUTER CONSULTANCY PTY LTD BE LIABLE
  20. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  26. // DAMAGE.
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Text;
  30. using System.Threading;
  31. using Interlace.Collections;
  32. using Interlace.ReactorCore;
  33. #endregion
  34. namespace Interlace.ReactorService
  35. {
  36. public class ServiceHost : IDisposable
  37. {
  38. Thread _thread = null;
  39. List<IService> _services = new List<IService>();
  40. List<IService> _permanentServices = new List<IService>();
  41. ServiceState _state = ServiceState.Down;
  42. ServiceStateRequest _stateRequest = ServiceStateRequest.None;
  43. ManualResetEvent _stateRequestEvent;
  44. ServiceStateRequest _pendingRequest = ServiceStateRequest.None;
  45. TimeSpan _startingExceptionWaitTimeout = new TimeSpan(0, 1, 0);
  46. TimeSpan _upExceptionWaitTimeout = new TimeSpan(0, 1, 0);
  47. TimeSpan _serviceHostStopTimeout = new TimeSpan(0, 0, 30);
  48. DateTime _startingExceptionWaitFinishAt;
  49. DateTime _upExceptionWaitFinishAt;
  50. object _stateRequestLock = new object();
  51. Dictionary<string, object> _environment = null;
  52. Dictionary<string, object> _defaultEnvironment = null;
  53. public event EventHandler<ServiceExceptionEventArgs> UnhandledException;
  54. public event EventHandler<ServiceHostStateChangedEventArgs> StateChanged;
  55. Reactor _reactor;
  56. string _description;
  57. public ServiceHost()
  58. {
  59. _defaultEnvironment = new Dictionary<string, object>();
  60. _stateRequestEvent = new ManualResetEvent(false);
  61. _reactor = new Reactor();
  62. _reactor.ReactorException += new EventHandler<ServiceExceptionEventArgs>(_reactor_ReactorException);
  63. _reactor.AddPermanentHandle(_stateRequestEvent, StateRequestEventFired);
  64. _reactor.AddRepeatingTimer(new TimeSpan(0, 0, 10), ExceptionWaitTimer, null);
  65. }
  66. public TimeSpan StartingExceptionWaitTimeout
  67. {
  68. get { return _startingExceptionWaitTimeout; }
  69. set { _startingExceptionWaitTimeout = value; }
  70. }
  71. public TimeSpan UpExceptionWaitTimeout
  72. {
  73. get { return _upExceptionWaitTimeout; }
  74. set { _upExceptionWaitTimeout = value; }
  75. }
  76. void _reactor_ReactorException(object sender, ServiceExceptionEventArgs e)
  77. {
  78. if (UnhandledException != null) UnhandledException(this,
  79. new ServiceExceptionEventArgs(e.Kind, e.Exception));
  80. if (_state == ServiceState.Up)
  81. {
  82. CloseAll();
  83. }
  84. SetState(ServiceState.UpExceptionWait);
  85. _upExceptionWaitFinishAt = DateTime.Now + _upExceptionWaitTimeout;
  86. e.Handled = true;
  87. }
  88. public Dictionary<string, object> DefaultEnvironment
  89. {
  90. get { return _defaultEnvironment; }
  91. }
  92. public void Dispose()
  93. {
  94. if (_stateRequestEvent != null) _stateRequestEvent.Close();
  95. _stateRequestEvent = null;
  96. }
  97. public void AddService(IService service)
  98. {
  99. if (_thread != null)
  100. {
  101. throw new InvalidOperationException(
  102. "A service can not be added to the service host after it has been started.");
  103. }
  104. _services.Add(service);
  105. }
  106. public void AddPermanentService(IService service)
  107. {
  108. if (_thread != null)
  109. {
  110. throw new InvalidOperationException(
  111. "A permanent service can not be added to the service host after it has been started.");
  112. }
  113. _permanentServices.Add(service);
  114. }
  115. public void StartServiceHost()
  116. {
  117. // Do an initial sorting; as long as no permanent services add services,
  118. // any sorting problems should be thrown here:
  119. SortServices(_services);
  120. SortServices(_permanentServices);
  121. _thread = new Thread(ThreadMain);
  122. _thread.Name = _description;
  123. _thread.Start();
  124. }
  125. void StateRequestEventFired(IAsyncResult result, object state)
  126. {
  127. lock (_stateRequestLock)
  128. {
  129. _pendingRequest = _stateRequest;
  130. _stateRequest = ServiceStateRequest.None;
  131. _stateRequestEvent.Reset();
  132. }
  133. }
  134. void ExceptionWaitTimer(DateTime fireAt, object state)
  135. {
  136. if (_pendingRequest == ServiceStateRequest.None)
  137. {
  138. if (_state == ServiceState.StartingExceptionWait)
  139. {
  140. if (DateTime.Now > _startingExceptionWaitFinishAt) OpenServices();
  141. }
  142. else if (_state == ServiceState.UpExceptionWait)
  143. {
  144. if (DateTime.Now > _upExceptionWaitFinishAt) OpenServices();
  145. }
  146. }
  147. }
  148. void ThreadMain()
  149. {
  150. ServiceHostImplementation host = new ServiceHostImplementation(_reactor, _defaultEnvironment, _services);
  151. bool permanentServicesOpened = OpenListOfServices(_permanentServices, host,
  152. ServiceExceptionKind.DuringPermanentOpen, ServiceExceptionKind.DuringPermanentOpenAbort);
  153. try
  154. {
  155. SortServices(_services);
  156. }
  157. catch (Exception ex)
  158. {
  159. if (UnhandledException != null) UnhandledException(this,
  160. new ServiceExceptionEventArgs(ServiceExceptionKind.DuringPermanentOpen, ex));
  161. permanentServicesOpened = false;
  162. }
  163. bool running = true;
  164. while (running)
  165. {
  166. _reactor.RunLoopIteration();
  167. ServiceStateRequest request = _pendingRequest;
  168. _pendingRequest = ServiceStateRequest.None;
  169. switch (request)
  170. {
  171. case ServiceStateRequest.Open:
  172. if (permanentServicesOpened) OpenWasRequested();
  173. break;
  174. case ServiceStateRequest.Close:
  175. if (permanentServicesOpened) CloseWasRequested();
  176. break;
  177. case ServiceStateRequest.Shutdown:
  178. running = false;
  179. break;
  180. }
  181. }
  182. if (_state == ServiceState.Up)
  183. {
  184. CloseAll();
  185. _state = ServiceState.Down;
  186. }
  187. if (permanentServicesOpened) CloseListOfServices(_permanentServices, host, ServiceExceptionKind.DuringPermanentClose);
  188. }
  189. public void StopServiceHost()
  190. {
  191. lock (_stateRequestLock)
  192. {
  193. _stateRequest = ServiceStateRequest.Shutdown;
  194. _stateRequestEvent.Set();
  195. }
  196. bool joined = _thread.Join(_serviceHostStopTimeout);
  197. if (!joined)
  198. {
  199. _thread.Abort();
  200. throw new InvalidOperationException(
  201. "The service host thread failed to shut down in the specified time.");
  202. }
  203. }
  204. /// <summary>
  205. /// Attempts to bring the service up, or to restart it if it is already
  206. /// started.
  207. /// </summary>
  208. public void OpenServices()
  209. {
  210. lock (_stateRequestLock)
  211. {
  212. if (_stateRequest != ServiceStateRequest.Shutdown)
  213. {
  214. _stateRequest = ServiceStateRequest.Open;
  215. _stateRequestEvent.Set();
  216. }
  217. }
  218. }
  219. /// <summary>
  220. /// Attempts to shutdown the services.
  221. /// </summary>
  222. public void CloseServices()
  223. {
  224. lock (_stateRequestLock)
  225. {
  226. if (_stateRequest != ServiceStateRequest.Shutdown)
  227. {
  228. _stateRequest = ServiceStateRequest.Close;
  229. _stateRequestEvent.Set();
  230. }
  231. }
  232. }
  233. void SetState(ServiceState newState)
  234. {
  235. ServiceState oldState = _state;
  236. _state = newState;
  237. try
  238. {
  239. if (StateChanged != null) StateChanged(this, new ServiceHostStateChangedEventArgs(oldState, newState));
  240. }
  241. catch (Exception)
  242. {
  243. // Ignore exceptions; doing anything else is liable to cause problems in one
  244. // of the many places the state is changed.
  245. }
  246. }
  247. void OpenWasRequested()
  248. {
  249. bool successful = false;
  250. switch (_state)
  251. {
  252. case ServiceState.Down:
  253. SetState(ServiceState.Starting);
  254. successful = OpenAll();
  255. break;
  256. case ServiceState.StartingExceptionWait:
  257. SetState(ServiceState.Starting);
  258. successful = OpenAll();
  259. break;
  260. case ServiceState.Up:
  261. SetState(ServiceState.Restarting);
  262. CloseAll();
  263. successful = OpenAll();
  264. break;
  265. case ServiceState.UpExceptionWait:
  266. SetState(ServiceState.Restarting);
  267. successful = OpenAll();
  268. break;
  269. }
  270. if (successful)
  271. {
  272. SetState(ServiceState.Up);
  273. }
  274. else
  275. {
  276. SetState(ServiceState.StartingExceptionWait);
  277. _startingExceptionWaitFinishAt = DateTime.Now + _startingExceptionWaitTimeout;
  278. }
  279. }
  280. void CloseWasRequested()
  281. {
  282. switch (_state)
  283. {
  284. case ServiceState.Down:
  285. break;
  286. case ServiceState.StartingExceptionWait:
  287. // Do nothing.
  288. SetState(ServiceState.Down);
  289. break;
  290. case ServiceState.Up:
  291. SetState(ServiceState.Stopping);
  292. CloseAll();
  293. SetState(ServiceState.Down);
  294. break;
  295. case ServiceState.UpExceptionWait:
  296. // Do nothing.
  297. SetState(ServiceState.Down);
  298. break;
  299. }
  300. }
  301. static void SortServices(List<IService> services)
  302. {
  303. // Build a list of the services that also implement IServiceDependencies:
  304. List<IServiceDependencies> dependenciesList = new List<IServiceDependencies>();
  305. foreach (IService service in services)
  306. {
  307. if (service is IServiceDependencies)
  308. {
  309. IServiceDependencies dependencies = service as IServiceDependencies;
  310. dependenciesList.Add(dependencies);
  311. }
  312. }
  313. // Build a dictionary of service tags to services:
  314. Dictionary<string, IService> serviceTags = new Dictionary<string, IService>();
  315. foreach (IServiceDependencies dependencies in dependenciesList)
  316. {
  317. foreach (string serviceTag in dependencies.ProvidesServiceTags)
  318. {
  319. if (serviceTags.ContainsKey(serviceTag))
  320. {
  321. throw new ServiceHostException(string.Format(
  322. "Multiple services are advertising that they provide the \"{0}\" service.",
  323. serviceTag));
  324. }
  325. serviceTags[serviceTag] = dependencies as IService;
  326. }
  327. }
  328. // Build a map from required services to requiring services:
  329. Dictionary<IService, Set<IService>> dependsOn = new Dictionary<IService, Set<IService>>();
  330. foreach (IServiceDependencies dependencies in dependenciesList)
  331. {
  332. foreach (string serviceTag in dependencies.RequiresServiceTags)
  333. {
  334. if (!serviceTags.ContainsKey(serviceTag))
  335. {
  336. throw new ServiceHostException(string.Format(
  337. "No service is providing the \"{0}\" service tag.", serviceTag));
  338. }
  339. IService requiredService = serviceTags[serviceTag];
  340. if (!dependsOn.ContainsKey(requiredService))
  341. {
  342. dependsOn[requiredService] = new Set<IService>();
  343. }
  344. dependsOn[requiredService].UnionUpdate(dependencies as IService);
  345. }
  346. }
  347. // Sort the services:
  348. ICollection<IService> sortedServices = TopologicalSort.Sort(services,
  349. delegate(IService service)
  350. {
  351. Set<IService> dependsOnSet;
  352. if (dependsOn.TryGetValue(service, out dependsOnSet))
  353. {
  354. return (IEnumerable<IService>)dependsOnSet;
  355. }
  356. else
  357. {
  358. return (IEnumerable<IService>)new IService[] { };
  359. }
  360. });
  361. services.Clear();
  362. services.AddRange(sortedServices);
  363. }
  364. bool OpenListOfServices(List<IService> services, IServiceHost host,
  365. ServiceExceptionKind openExceptionKind, ServiceExceptionKind openAbortExceptionKind)
  366. {
  367. Stack<IService> startedServices = new Stack<IService>();
  368. try
  369. {
  370. foreach (IService service in services)
  371. {
  372. service.Open(host);
  373. startedServices.Push(service);
  374. }
  375. return true;
  376. }
  377. catch (Exception ex)
  378. {
  379. while (startedServices.Count > 0)
  380. {
  381. IService serviceToShutdown = startedServices.Pop();
  382. try
  383. {
  384. serviceToShutdown.Close(host);
  385. }
  386. catch (Exception nestedEx)
  387. {
  388. if (UnhandledException != null) UnhandledException(this,
  389. new ServiceExceptionEventArgs(openAbortExceptionKind, nestedEx));
  390. }
  391. }
  392. if (UnhandledException != null) UnhandledException(this,
  393. new ServiceExceptionEventArgs(openExceptionKind, ex));
  394. return false;
  395. }
  396. }
  397. void CloseListOfServices(List<IService> services, IServiceHost host,
  398. ServiceExceptionKind closeExceptionKind)
  399. {
  400. List<IService> reverseList = new List<IService>(services);
  401. reverseList.Reverse();
  402. foreach (IService service in reverseList)
  403. {
  404. try
  405. {
  406. service.Close(host);
  407. }
  408. catch (Exception ex)
  409. {
  410. if (UnhandledException != null) UnhandledException(this,
  411. new ServiceExceptionEventArgs(closeExceptionKind, ex));
  412. }
  413. }
  414. }
  415. bool OpenAll()
  416. {
  417. _environment = new Dictionary<string, object>();
  418. IServiceHost host = new ServiceHostImplementation(_reactor, _environment, null);
  419. foreach (KeyValuePair<string, object> pair in _defaultEnvironment)
  420. {
  421. _environment.Add(pair.Key, pair.Value);
  422. }
  423. return OpenListOfServices(_services, host, ServiceExceptionKind.DuringOpen, ServiceExceptionKind.DuringOpenAbort);
  424. }
  425. void CloseAll()
  426. {
  427. IServiceHost host = new ServiceHostImplementation(_reactor, _environment, null);
  428. CloseListOfServices(_services, host, ServiceExceptionKind.DuringClose);
  429. _environment = null;
  430. }
  431. public string Description
  432. {
  433. get { return _description; }
  434. set { _description = value; }
  435. }
  436. }
  437. }