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