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

/GPX.Server.Extension/GeoRSSClient/GeoRSSClientService.cs

https://bitbucket.org/shope/dfu
C# | 498 lines | 340 code | 102 blank | 56 comment | 19 complexity | efe8dbe7a9c5ab02643831b812beae52 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using System.ServiceProcess;
  8. using System.Text;
  9. using System.Configuration;
  10. using System.Xml.Linq;
  11. using log4net;
  12. using System.IO;
  13. using System.Timers;
  14. using System.Net;
  15. using Newtonsoft.Json.Linq;
  16. using System.ServiceModel.Syndication;
  17. using System.Xml;
  18. using System.Security;
  19. namespace GeoRSSClient
  20. {
  21. public partial class GeoRSSClientService : ServiceBase
  22. {
  23. private int _interval;
  24. private string _feedConfigFilePath;
  25. private string _logFilePath;
  26. private XDocument _feedConfigFile;
  27. IEnumerable<GeoRSSFeed> _Enumerablefeeds;
  28. List<GeoRSSFeed> newFeeds;
  29. protected static readonly ILog log = LogManager.GetLogger(typeof(GeoRSSClientService));
  30. private const string ATOM_NS = "http://www.w3.org/2005/Atom";
  31. private const string ATOM_ENTRY = "entry";
  32. private const string RSS_NS = "http://www.w3.org/2005/Atom"; //todo - test merging rss formatted feeds
  33. private const string RSS_ENTRY = "item";
  34. public GeoRSSClientService()
  35. {
  36. InitializeComponent();
  37. }
  38. /// <summary>
  39. /// When implemented in a derived class, executes when a Start command is sent to the service by the Service Control Manager (SCM) or when the operating system starts (for a service that starts automatically). Specifies actions to take when the service starts.
  40. /// </summary>
  41. /// <param name="args">Data passed by the start command.</param>
  42. protected override void OnStart(string[] args)
  43. {
  44. try
  45. {
  46. //read the configuration
  47. log.Info("Reading Client Configuration");
  48. foreach (string key in ConfigurationManager.AppSettings)
  49. {
  50. switch (key)
  51. {
  52. case "Frequency":
  53. _interval = Convert.ToInt32(ConfigurationManager.AppSettings[key]);
  54. break;
  55. case "ConfigurationFile":
  56. _feedConfigFilePath = ConfigurationManager.AppSettings[key];
  57. break;
  58. case "LogConfigurationFile":
  59. _logFilePath = ConfigurationManager.AppSettings[key];
  60. break;
  61. default:
  62. break;
  63. }
  64. }
  65. log.Info("Reading Client Configuration Complete");
  66. //initialise the event log
  67. log4net.Config.XmlConfigurator.Configure(new FileInfo(_logFilePath));
  68. log.Info("Initialised Log");
  69. log.Info("Starting Timer - configuration will be re-read and feeds will be requested every " + _interval / 60000 + " minutes");
  70. System.Timers.Timer aTimer = new System.Timers.Timer();
  71. aTimer.Elapsed += new ElapsedEventHandler(RequestFeedsTimerHandler);
  72. aTimer.Interval = _interval;
  73. aTimer.Enabled = true;
  74. }
  75. catch (Exception ex)
  76. {
  77. log.Error("An error occured" + ex.Message);
  78. log.Fatal(ex.StackTrace);
  79. throw ex;
  80. }
  81. finally
  82. {
  83. }
  84. }
  85. /// <summary>
  86. /// Requests the feeds timer handler.
  87. /// </summary>
  88. /// <param name="source">The source.</param>
  89. /// <param name="e">The <see cref="System.Timers.ElapsedEventArgs"/> instance containing the event data.</param>
  90. private void RequestFeedsTimerHandler(object source, ElapsedEventArgs e)
  91. {
  92. RequestFeeds();
  93. WriteFeed();
  94. }
  95. /// <summary>
  96. /// Writes the feed.
  97. /// </summary>
  98. /// <param name="ns">The ns.</param>
  99. /// <param name="itemName">Name of the item.</param>
  100. /// <remarks>The namespace and item name is used to locate the correct type of items from sub feeds to merge into the primary feed</remarks>
  101. private void WriteFeed()
  102. {
  103. try
  104. {
  105. XmlDocument primaryFeed;
  106. foreach (GeoRSSFeed feed in newFeeds)
  107. {
  108. //get the feedformat and set the correct namespace and entry
  109. XNamespace ns;
  110. string itemName;
  111. if (feed.FeedFormat == "atom")
  112. {
  113. ns = ATOM_NS;
  114. itemName = ATOM_ENTRY;
  115. }
  116. else
  117. {
  118. ns = RSS_NS;
  119. itemName = RSS_ENTRY;
  120. }
  121. //set primary feed
  122. log.Info("Checking Primary Feed");
  123. primaryFeed = GetPrimarySubFeed(feed);
  124. if (primaryFeed != null)
  125. {
  126. var primaryFeedXDoc = primaryFeed.ToXDocument();
  127. log.Info("Merging Feeds");
  128. foreach (GeoRSSSubFeed subFeed in feed.SubFeeds)
  129. {
  130. if (!subFeed.Primary)
  131. {
  132. //append all items from subfeed to primary feed items collection
  133. XmlDocument subFeedXmlDoc = new XmlDocument();
  134. subFeedXmlDoc.LoadXml(subFeed.FeedContent);
  135. var subFeedXDoc = subFeedXmlDoc.ToXDocument();
  136. //select all items i.e.
  137. var entries = subFeedXDoc.Descendants(ns + itemName);
  138. List<XElement> subfeedItems = entries.ToList();
  139. //iterate over the subfeed elements and add to the primary feed elements
  140. foreach (XElement el in subfeedItems)
  141. {
  142. XElement lastEntry = primaryFeedXDoc.Descendants(ns + itemName).Last();
  143. lastEntry.AddAfterSelf(el);
  144. }
  145. //reset the primary feed
  146. primaryFeed = primaryFeedXDoc.ToXmlDocument();
  147. }
  148. }
  149. //write to file
  150. log.Info("Ready to write file ");
  151. System.IO.StreamWriter file = new System.IO.StreamWriter(feed.OutputPath + feed.FeedName + ".xml",false);
  152. file.WriteLine(primaryFeed.OuterXml);
  153. file.Close();
  154. }
  155. }
  156. }
  157. catch (UnauthorizedAccessException ex)
  158. {
  159. log.Error("A error occurred merging or writing the file UnauthorizedAccessException " + ex.Message);
  160. }
  161. catch (ArgumentException ex)
  162. {
  163. log.Error("A error occurred merging or writing the file ArgumentException " + ex.Message);
  164. }
  165. catch (DirectoryNotFoundException ex)
  166. {
  167. log.Error("A error occurred merging or writing the file DirectoryNotFoundException " + ex.Message);
  168. }
  169. catch (PathTooLongException ex)
  170. {
  171. log.Error("A error occurred merging or writing the file PathTooLongException " + ex.Message);
  172. }
  173. catch (IOException ex)
  174. {
  175. log.Error("A error occurred merging or writing the file IOException " + ex.Message);
  176. }
  177. catch (SecurityException ex)
  178. {
  179. log.Error("A error occurred merging or writing the file SecurityException " + ex.Message);
  180. }
  181. catch (Exception ex)
  182. {
  183. log.Error("A error occurred merging or writing the file " + ex.Message);
  184. }
  185. finally
  186. {
  187. }
  188. }
  189. /// <summary>
  190. /// Requests the feeds.
  191. /// </summary>
  192. private void RequestFeeds()
  193. {
  194. try
  195. {
  196. log.Info("Requesting Feeds");
  197. ReadFeedConfiguration();
  198. log.Info("Feed configuration contains: " + newFeeds.Count.ToString() + " feeds");
  199. foreach (GeoRSSFeed feed in newFeeds)
  200. {
  201. log.Info("requesting feed data " + feed.FeedName);
  202. foreach (GeoRSSSubFeed subFeed in feed.SubFeeds)
  203. {
  204. log.Info("requesting sub feed data " + subFeed.FeedName);
  205. if (subFeed.Configuration != null)
  206. {
  207. string url = subFeed.LayerUrl;
  208. StringBuilder sb = new StringBuilder();
  209. sb.Append("filterGeometry=" + subFeed.Configuration.FilterGeometry.ToString());
  210. sb.Append("&geometryType=" + subFeed.Configuration.FilterGeometryType);
  211. sb.Append("&where=" + subFeed.Configuration.WhereClause);
  212. sb.Append("&exportProperties=" + subFeed.Configuration.ExportProperties.ToString());
  213. sb.Append("&f=georss");
  214. string response;
  215. CallArcGISServer(url, sb.ToString(), out response, subFeed.Authenticated, subFeed.UserName, subFeed.Password);
  216. log.Debug(response);
  217. if (!String.IsNullOrEmpty(response))
  218. {
  219. subFeed.FeedContent = response;
  220. }
  221. }
  222. else
  223. {
  224. throw new Exception("sub feed " + subFeed.FeedName + " configuration is null");
  225. }
  226. log.Info("requesting sub feed data " + subFeed.FeedName + " complete");
  227. }
  228. log.Info("requesting feed data " + feed.FeedName + " complete");
  229. }
  230. }
  231. catch (Exception ex)
  232. {
  233. log.Error("A error occurred requesting feed data " + ex.Message);
  234. }
  235. finally
  236. {
  237. }
  238. }
  239. /// <summary>
  240. /// Reads the feed configuration.
  241. /// </summary>
  242. private void ReadFeedConfiguration()
  243. {
  244. try
  245. {
  246. if (!String.IsNullOrEmpty(_feedConfigFilePath))
  247. {
  248. log.Info("Loading Feed Configuration from: " + _feedConfigFilePath);
  249. _feedConfigFile = XDocument.Load(_feedConfigFilePath);
  250. }
  251. // read the feed definitions
  252. _Enumerablefeeds = from feed in _feedConfigFile.Descendants("feed")
  253. select new GeoRSSFeed(feed.Attribute("name").Value, feed.Attribute("outputpath").Value, feed.Attribute("feedFormat").Value)
  254. {
  255. SubFeeds = (from subfeed in feed.Elements("subfeed")
  256. select new GeoRSSSubFeed(
  257. Convert.ToBoolean(subfeed.Attribute("primary").Value),
  258. subfeed.Attribute("configuration").Value,
  259. subfeed.Attribute("name").Value, subfeed.Attribute("layerurl").Value,
  260. Convert.ToBoolean(subfeed.Attribute("authenticated").Value),
  261. subfeed.Attribute("username").Value,
  262. subfeed.Attribute("password").Value)
  263. ).ToList()
  264. };
  265. newFeeds = _Enumerablefeeds.ToList();
  266. log.Info("Loading Feed Configuration from: " + _feedConfigFilePath + " complete");
  267. if (newFeeds != null)
  268. {
  269. foreach (GeoRSSFeed feed in newFeeds)
  270. {
  271. log.Info("Initialising feed configuration for feed" + feed.FeedName);
  272. foreach (GeoRSSSubFeed subFeed in feed.SubFeeds)
  273. {
  274. log.Info("Initialising sub feed configuration for sub feed" + subFeed.FeedName);
  275. //read the feed json parameters
  276. //todo - add protection for file not found exceptions
  277. System.IO.StreamReader myFile = new System.IO.StreamReader(subFeed.ConfigurationFile);
  278. string feedParametersAsString = myFile.ReadToEnd();
  279. myFile.Close();
  280. //log.Info("initialising sub feed configuration with value: " + feedParametersAsString); //todo remove this log line
  281. subFeed.Configuration = new GeoRSSConfiguration(feedParametersAsString, log);
  282. log.Info("Initialising sub feed configuration for sub feed" + subFeed.FeedName + " complete");
  283. }
  284. log.Info("Initialising feed configuration for feed" + feed.FeedName + " complete");
  285. }
  286. }
  287. }
  288. catch (Exception ex)
  289. {
  290. log.Error("An error occured" + ex.Message);
  291. log.Fatal(ex.StackTrace);
  292. throw ex;
  293. }
  294. finally
  295. {
  296. }
  297. }
  298. /// <summary>
  299. /// Gets the primary sub feed.
  300. /// </summary>
  301. /// <param name="feed">The feed.</param>
  302. /// <returns></returns>
  303. private static XmlDocument GetPrimarySubFeed(GeoRSSFeed feed)
  304. {
  305. XmlDocument primaryFeed = null;
  306. try
  307. {
  308. log.Info("Getting primary sub feed " + feed.FeedName);
  309. foreach (GeoRSSSubFeed subFeed in feed.SubFeeds)
  310. {
  311. if (subFeed.Primary)
  312. {
  313. if (!string.IsNullOrEmpty(subFeed.FeedContent))
  314. {
  315. primaryFeed = new XmlDocument();
  316. primaryFeed.LoadXml(subFeed.FeedContent);
  317. }
  318. break;
  319. }
  320. }
  321. return primaryFeed;
  322. }
  323. catch (Exception ex)
  324. {
  325. log.Error("An error occured getting the primary feed" + ex.Message);
  326. throw;
  327. }
  328. finally
  329. {
  330. }
  331. }
  332. //todo - make asynch
  333. /// <summary>
  334. /// Calls the arc GIS server.
  335. /// </summary>
  336. /// <param name="URI">The URI.</param>
  337. /// <param name="postData">The post data.</param>
  338. /// <param name="response">The response.</param>
  339. private void CallArcGISServer(string URI, string postData, out string response, bool authenticated, string username, string password)
  340. {
  341. Stream data = null;
  342. StreamReader reader = null;
  343. try
  344. {
  345. //log the request string
  346. log.Info("Sending request to AGS: " + URI);
  347. // The ArcGIS Server Rest Extension is set to recieve a POSt
  348. WebRequest request = WebRequest.Create(URI);
  349. //DFU-39 - adding support for authentication
  350. if (authenticated)
  351. {
  352. log.Info("URL is authenticated creating new credential with username " + username);
  353. //SH - the following code was used - however I have chosen to not force the header to use Basic and let the web server challenge
  354. //string authInfo = username + ":" + password;
  355. //authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
  356. //request.Headers["Authorization"] = "Basic " + authInfo;
  357. request.Credentials = new NetworkCredential(username, password);
  358. }
  359. request.Method = "POST";
  360. log.Debug("Sending request post data to AGS " + postData);
  361. byte[] byteArray = Encoding.UTF8.GetBytes(postData);
  362. request.ContentType = "application/x-www-form-urlencoded";
  363. request.ContentLength = byteArray.Length;
  364. Stream dataStream = request.GetRequestStream();
  365. dataStream.Write(byteArray, 0, byteArray.Length);
  366. dataStream.Close();
  367. // Get the response.
  368. WebResponse serverResponse = request.GetResponse();
  369. log.Info("Response Status: " + ((HttpWebResponse)serverResponse).StatusDescription);
  370. // Get the stream containing content returned by the server.
  371. dataStream = serverResponse.GetResponseStream();
  372. reader = new StreamReader(dataStream);
  373. response = reader.ReadToEnd();
  374. // Clean up the streams.
  375. reader.Close();
  376. dataStream.Close();
  377. serverResponse.Close();
  378. }
  379. catch (Exception ex)
  380. {
  381. log.Info("An error occured when calling ArcGIS Server" + ex.ToString());
  382. throw;
  383. }
  384. finally
  385. {
  386. if (data != null)
  387. data.Close();
  388. if (reader != null)
  389. reader.Close();
  390. }
  391. }
  392. protected override void OnStop()
  393. {
  394. }
  395. }
  396. }