/GPX.Server.Extension/GeoRSSClient/GeoRSSClientService.cs
C# | 498 lines | 340 code | 102 blank | 56 comment | 19 complexity | efe8dbe7a9c5ab02643831b812beae52 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Diagnostics;
- using System.Linq;
- using System.ServiceProcess;
- using System.Text;
- using System.Configuration;
- using System.Xml.Linq;
- using log4net;
- using System.IO;
- using System.Timers;
- using System.Net;
- using Newtonsoft.Json.Linq;
- using System.ServiceModel.Syndication;
- using System.Xml;
- using System.Security;
-
- namespace GeoRSSClient
- {
- public partial class GeoRSSClientService : ServiceBase
- {
- private int _interval;
- private string _feedConfigFilePath;
- private string _logFilePath;
- private XDocument _feedConfigFile;
- IEnumerable<GeoRSSFeed> _Enumerablefeeds;
- List<GeoRSSFeed> newFeeds;
- protected static readonly ILog log = LogManager.GetLogger(typeof(GeoRSSClientService));
-
- private const string ATOM_NS = "http://www.w3.org/2005/Atom";
- private const string ATOM_ENTRY = "entry";
- private const string RSS_NS = "http://www.w3.org/2005/Atom"; //todo - test merging rss formatted feeds
- private const string RSS_ENTRY = "item";
-
-
-
- public GeoRSSClientService()
- {
- InitializeComponent();
- }
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="args">Data passed by the start command.</param>
- protected override void OnStart(string[] args)
- {
- try
- {
-
- //read the configuration
- log.Info("Reading Client Configuration");
- foreach (string key in ConfigurationManager.AppSettings)
- {
- switch (key)
- {
- case "Frequency":
- _interval = Convert.ToInt32(ConfigurationManager.AppSettings[key]);
- break;
- case "ConfigurationFile":
- _feedConfigFilePath = ConfigurationManager.AppSettings[key];
- break;
- case "LogConfigurationFile":
- _logFilePath = ConfigurationManager.AppSettings[key];
- break;
- default:
- break;
- }
- }
- log.Info("Reading Client Configuration Complete");
-
- //initialise the event log
- log4net.Config.XmlConfigurator.Configure(new FileInfo(_logFilePath));
- log.Info("Initialised Log");
-
-
- log.Info("Starting Timer - configuration will be re-read and feeds will be requested every " + _interval / 60000 + " minutes");
- System.Timers.Timer aTimer = new System.Timers.Timer();
- aTimer.Elapsed += new ElapsedEventHandler(RequestFeedsTimerHandler);
- aTimer.Interval = _interval;
- aTimer.Enabled = true;
-
- }
- catch (Exception ex)
- {
- log.Error("An error occured" + ex.Message);
- log.Fatal(ex.StackTrace);
- throw ex;
- }
- finally
- {
-
- }
-
- }
-
-
- /// <summary>
- /// Requests the feeds timer handler.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="e">The <see cref="System.Timers.ElapsedEventArgs"/> instance containing the event data.</param>
- private void RequestFeedsTimerHandler(object source, ElapsedEventArgs e)
- {
- RequestFeeds();
-
- WriteFeed();
- }
-
- /// <summary>
- /// Writes the feed.
- /// </summary>
- /// <param name="ns">The ns.</param>
- /// <param name="itemName">Name of the item.</param>
- /// <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>
- private void WriteFeed()
- {
- try
- {
- XmlDocument primaryFeed;
-
- foreach (GeoRSSFeed feed in newFeeds)
- {
-
- //get the feedformat and set the correct namespace and entry
- XNamespace ns;
- string itemName;
- if (feed.FeedFormat == "atom")
- {
- ns = ATOM_NS;
- itemName = ATOM_ENTRY;
- }
- else
- {
- ns = RSS_NS;
- itemName = RSS_ENTRY;
- }
-
- //set primary feed
- log.Info("Checking Primary Feed");
- primaryFeed = GetPrimarySubFeed(feed);
-
- if (primaryFeed != null)
- {
- var primaryFeedXDoc = primaryFeed.ToXDocument();
-
-
- log.Info("Merging Feeds");
- foreach (GeoRSSSubFeed subFeed in feed.SubFeeds)
- {
- if (!subFeed.Primary)
- {
- //append all items from subfeed to primary feed items collection
- XmlDocument subFeedXmlDoc = new XmlDocument();
- subFeedXmlDoc.LoadXml(subFeed.FeedContent);
- var subFeedXDoc = subFeedXmlDoc.ToXDocument();
-
-
- //select all items i.e.
- var entries = subFeedXDoc.Descendants(ns + itemName);
-
-
- List<XElement> subfeedItems = entries.ToList();
-
- //iterate over the subfeed elements and add to the primary feed elements
- foreach (XElement el in subfeedItems)
- {
- XElement lastEntry = primaryFeedXDoc.Descendants(ns + itemName).Last();
- lastEntry.AddAfterSelf(el);
- }
-
- //reset the primary feed
- primaryFeed = primaryFeedXDoc.ToXmlDocument();
-
- }
-
- }
-
- //write to file
- log.Info("Ready to write file ");
- System.IO.StreamWriter file = new System.IO.StreamWriter(feed.OutputPath + feed.FeedName + ".xml",false);
- file.WriteLine(primaryFeed.OuterXml);
-
- file.Close();
- }
- }
- }
- catch (UnauthorizedAccessException ex)
- {
- log.Error("A error occurred merging or writing the file UnauthorizedAccessException " + ex.Message);
- }
- catch (ArgumentException ex)
- {
- log.Error("A error occurred merging or writing the file ArgumentException " + ex.Message);
- }
- catch (DirectoryNotFoundException ex)
- {
- log.Error("A error occurred merging or writing the file DirectoryNotFoundException " + ex.Message);
- }
- catch (PathTooLongException ex)
- {
- log.Error("A error occurred merging or writing the file PathTooLongException " + ex.Message);
- }
- catch (IOException ex)
- {
- log.Error("A error occurred merging or writing the file IOException " + ex.Message);
- }
- catch (SecurityException ex)
- {
- log.Error("A error occurred merging or writing the file SecurityException " + ex.Message);
- }
- catch (Exception ex)
- {
- log.Error("A error occurred merging or writing the file " + ex.Message);
- }
- finally
- {
-
- }
- }
-
- /// <summary>
- /// Requests the feeds.
- /// </summary>
- private void RequestFeeds()
- {
- try
- {
- log.Info("Requesting Feeds");
- ReadFeedConfiguration();
-
- log.Info("Feed configuration contains: " + newFeeds.Count.ToString() + " feeds");
-
- foreach (GeoRSSFeed feed in newFeeds)
- {
- log.Info("requesting feed data " + feed.FeedName);
-
-
- foreach (GeoRSSSubFeed subFeed in feed.SubFeeds)
- {
-
- log.Info("requesting sub feed data " + subFeed.FeedName);
-
-
-
-
- if (subFeed.Configuration != null)
- {
-
- string url = subFeed.LayerUrl;
-
- StringBuilder sb = new StringBuilder();
- sb.Append("filterGeometry=" + subFeed.Configuration.FilterGeometry.ToString());
- sb.Append("&geometryType=" + subFeed.Configuration.FilterGeometryType);
- sb.Append("&where=" + subFeed.Configuration.WhereClause);
- sb.Append("&exportProperties=" + subFeed.Configuration.ExportProperties.ToString());
- sb.Append("&f=georss");
-
- string response;
- CallArcGISServer(url, sb.ToString(), out response, subFeed.Authenticated, subFeed.UserName, subFeed.Password);
-
- log.Debug(response);
-
- if (!String.IsNullOrEmpty(response))
- {
- subFeed.FeedContent = response;
-
- }
-
- }
- else
- {
- throw new Exception("sub feed " + subFeed.FeedName + " configuration is null");
- }
-
- log.Info("requesting sub feed data " + subFeed.FeedName + " complete");
- }
-
- log.Info("requesting feed data " + feed.FeedName + " complete");
- }
- }
- catch (Exception ex)
- {
- log.Error("A error occurred requesting feed data " + ex.Message);
- }
- finally
- {
-
- }
-
- }
-
- /// <summary>
- /// Reads the feed configuration.
- /// </summary>
- private void ReadFeedConfiguration()
- {
- try
- {
-
- if (!String.IsNullOrEmpty(_feedConfigFilePath))
- {
- log.Info("Loading Feed Configuration from: " + _feedConfigFilePath);
-
- _feedConfigFile = XDocument.Load(_feedConfigFilePath);
- }
-
- // read the feed definitions
- _Enumerablefeeds = from feed in _feedConfigFile.Descendants("feed")
- select new GeoRSSFeed(feed.Attribute("name").Value, feed.Attribute("outputpath").Value, feed.Attribute("feedFormat").Value)
- {
- SubFeeds = (from subfeed in feed.Elements("subfeed")
- select new GeoRSSSubFeed(
- Convert.ToBoolean(subfeed.Attribute("primary").Value),
- subfeed.Attribute("configuration").Value,
- subfeed.Attribute("name").Value, subfeed.Attribute("layerurl").Value,
- Convert.ToBoolean(subfeed.Attribute("authenticated").Value),
- subfeed.Attribute("username").Value,
- subfeed.Attribute("password").Value)
- ).ToList()
- };
-
-
- newFeeds = _Enumerablefeeds.ToList();
-
- log.Info("Loading Feed Configuration from: " + _feedConfigFilePath + " complete");
-
- if (newFeeds != null)
- {
- foreach (GeoRSSFeed feed in newFeeds)
- {
- log.Info("Initialising feed configuration for feed" + feed.FeedName);
-
-
- foreach (GeoRSSSubFeed subFeed in feed.SubFeeds)
- {
-
- log.Info("Initialising sub feed configuration for sub feed" + subFeed.FeedName);
-
- //read the feed json parameters
- //todo - add protection for file not found exceptions
- System.IO.StreamReader myFile = new System.IO.StreamReader(subFeed.ConfigurationFile);
- string feedParametersAsString = myFile.ReadToEnd();
-
- myFile.Close();
-
- //log.Info("initialising sub feed configuration with value: " + feedParametersAsString); //todo remove this log line
-
- subFeed.Configuration = new GeoRSSConfiguration(feedParametersAsString, log);
-
- log.Info("Initialising sub feed configuration for sub feed" + subFeed.FeedName + " complete");
- }
-
- log.Info("Initialising feed configuration for feed" + feed.FeedName + " complete");
- }
-
- }
- }
- catch (Exception ex)
- {
- log.Error("An error occured" + ex.Message);
- log.Fatal(ex.StackTrace);
- throw ex;
- }
- finally
- {
-
- }
-
- }
-
- /// <summary>
- /// Gets the primary sub feed.
- /// </summary>
- /// <param name="feed">The feed.</param>
- /// <returns></returns>
- private static XmlDocument GetPrimarySubFeed(GeoRSSFeed feed)
- {
- XmlDocument primaryFeed = null;
-
- try
- {
-
- log.Info("Getting primary sub feed " + feed.FeedName);
-
- foreach (GeoRSSSubFeed subFeed in feed.SubFeeds)
- {
- if (subFeed.Primary)
- {
- if (!string.IsNullOrEmpty(subFeed.FeedContent))
- {
- primaryFeed = new XmlDocument();
- primaryFeed.LoadXml(subFeed.FeedContent);
- }
-
- break;
- }
-
- }
-
- return primaryFeed;
- }
- catch (Exception ex)
- {
- log.Error("An error occured getting the primary feed" + ex.Message);
- throw;
- }
- finally
- {
-
- }
- }
-
- //todo - make asynch
- /// <summary>
- /// Calls the arc GIS server.
- /// </summary>
- /// <param name="URI">The URI.</param>
- /// <param name="postData">The post data.</param>
- /// <param name="response">The response.</param>
- private void CallArcGISServer(string URI, string postData, out string response, bool authenticated, string username, string password)
- {
- Stream data = null;
- StreamReader reader = null;
-
- try
- {
- //log the request string
- log.Info("Sending request to AGS: " + URI);
-
-
- // The ArcGIS Server Rest Extension is set to recieve a POSt
- WebRequest request = WebRequest.Create(URI);
-
- //DFU-39 - adding support for authentication
- if (authenticated)
- {
- log.Info("URL is authenticated creating new credential with username " + username);
-
- //SH - the following code was used - however I have chosen to not force the header to use Basic and let the web server challenge
- //string authInfo = username + ":" + password;
- //authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
- //request.Headers["Authorization"] = "Basic " + authInfo;
- request.Credentials = new NetworkCredential(username, password);
- }
-
- request.Method = "POST";
-
- log.Debug("Sending request post data to AGS " + postData);
-
- byte[] byteArray = Encoding.UTF8.GetBytes(postData);
- request.ContentType = "application/x-www-form-urlencoded";
- request.ContentLength = byteArray.Length;
- Stream dataStream = request.GetRequestStream();
- dataStream.Write(byteArray, 0, byteArray.Length);
- dataStream.Close();
-
- // Get the response.
- WebResponse serverResponse = request.GetResponse();
-
- log.Info("Response Status: " + ((HttpWebResponse)serverResponse).StatusDescription);
-
- // Get the stream containing content returned by the server.
- dataStream = serverResponse.GetResponseStream();
- reader = new StreamReader(dataStream);
- response = reader.ReadToEnd();
-
- // Clean up the streams.
- reader.Close();
- dataStream.Close();
- serverResponse.Close();
-
- }
- catch (Exception ex)
- {
- log.Info("An error occured when calling ArcGIS Server" + ex.ToString());
- throw;
- }
- finally
- {
- if (data != null)
- data.Close();
-
- if (reader != null)
- reader.Close();
- }
-
- }
-
-
-
- protected override void OnStop()
- {
- }
- }
- }