Wednesday, August 5, 2009

A Polling Service


As mentioned, I am starting off the technical posts.

I found the following use of windows services very useful. Just wanted to share this with everyone.
There are certain situations where you need to monitor changes, and trigger actions, like changes in data in a database. Sure, you can use database triggers, but here is an alternative to using database triggers, which involves using a windows service.
I will not go over the basic steps of creating a windows service here. Rather, I will go through the idea of polling for information in a windows service. If interested in the basic structure of a windows service, this article in Codeproject is a good source of information.
A service class is created. Apologies for the (lack of) indentation - I blame the Post editor :-


   1:  public class MonitorService : System.ServiceProcess.ServiceBase
   2:  {
   3:  /// 
   4:  /// Required designer variable.
   5:  private System.ComponentModel.Container components = null;
   6:  ServicePollingService pollingService;
   7:   
   8:   
   9:  public MonitorService()
  10:  {
  11:  // This call is required by the Windows.Forms Component Designer.
  12:  InitializeComponent();
  13:  }
  14:   
  15:   
  16:  // The main entry point for the process
  17:  static void Main()
  18:  {
  19:  System.ServiceProcess.ServiceBase[] ServicesToRun;
  20:   
  21:  // More than one user Service may run within the same process. To add
  22:  // another service to this process, change the following line to
  23:  // create a second service object. For example,
  24:  //
  25:  // ServicesToRun = new System.ServiceProcess.ServiceBase[] {new Service1(), new MySecondUserService()};
  26:  ServicesToRun = new System.ServiceProcess.ServiceBase[] { new PollingService() };
  27:  System.ServiceProcess.ServiceBase.Run(ServicesToRun);
  28:  }/// 
  29:  /// Required method for Designer support /// 
  30:   
  31:  private void InitializeComponent()
  32:  {
  33:  components = new System.ComponentModel.Container();
  34:  this.ServiceName = "Monitor Service";
  35:  }/// 
  36:  /// Clean up any resources being used.
  37:  protected override void Dispose( bool disposing )
  38:  {
  39:  if( disposing )
  40:  {
  41:  if (components != null)
  42:  {
  43:  components.Dispose();
  44:  }
  45:  }
  46:  base.Dispose( disposing );
  47:  }/// 
  48:  /// Set things in motion so your service can do its work.
  49:  protected override void OnStart(string[] args)
  50:  {
  51:  pollingService = new PollingService();
  52:  pollingService.Start();
  53:  }/// 
  54:  /// Suspends this service.
  55:  protected override void OnPause()
  56:  {
  57:  pollingService.Suspend();
  58:  }/// 
  59:  /// Resume this service.
  60:  protected override void OnContinue()
  61:  {
  62:  pollingService.Resume();
  63:  }/// 
  64:  /// Stop this service.
  65:  protected override void OnStop()
  66:  {
  67:  pollingService.Stop();
  68:  }
  69:  }
  70:   
Like most service classes, PollingService has a Start and a Stop method. The Start method starts a listener thread which monitors changes - it could periodically run another function/stored procedure, etc to "poll" or look for changes. This is shown below:


   1:  public class PollingService
   2:  {
   3:  private Thread listenerThread; 
   4:   
   5:   
   6:   
   7:   
   8:  // Response Server States.private const int STOPPED = 1;
   9:  private const int RUN = 2;
  10:  private const int STOP = 3;
  11:   
  12:   
  13:  private int state = STOPPED;
  14:  public const string ServiceName = "Polling Service";
  15:   
  16:  public PollingService()
  17:  {
  18:  }/// 
  19:  /// Starts the Listener thread.
  20:  public void Start()
  21:  {
  22:  listenerThread = new Thread(new ThreadStart(this.Listener));
  23:  listenerThread.Start();
  24:  }
  25:   
  26:  /// 
  27:  /// Stops the Listener thread.
  28:  public void Stop()
  29:  {
  30:   
  31:  // Signal to the worker thread that it should stop ...
  32:   
  33:  state = STOP;
  34:  listenerThread.Interrupt();
  35:  // Save required data here.
  36:  }
  37:   
  38:  /// 
  39:  /// Suspends the Listener thread.
  40:   
  41:  ///
  42:  public void Suspend()
  43:  {
  44:  listenerThread.Suspend();
  45:  // Save required data here.
  46:  }
  47:  /// 
  48:  /// Resumes current thread.
  49:  /// 
  50:   
  51:  public void Resume()
  52:  {
  53:  // Initialize again.
  54:  listenerThread.Resume();
  55:  }
  56:   
  57:  /// 
  58:  /// Listens for changes.
  59:  /// 
  60:   
  61:  protected void Listener()
  62:  {
  63:  try
  64:  {
  65:  state = RUN;
  66:  while ( state == RUN )
  67:  {
  68:  PollForChanges();
  69:   
  70:  // Where the intervals come from configuration.
  71:   
  72:  Thread.Sleep(new TimeSpan(hoursInterval, minutesInterval, secondsInterval));
  73:  }
  74:  }
  75:  catch ( ThreadInterruptedException )
  76:  {
  77:   
  78:  // No need to log the fact that the thread has been interrupted.}
  79:  catch ( Exception e)
  80:  {
  81:   
  82:  // Log to database or event log.
  83:   
  84:  LogHelper.LogError(e.ToString());
  85:  }
  86:  finally
  87:  {
  88:  state = STOPPED;
  89:  LogHelper.LogWarning(string.Format("The {0} has shutdown.", ServiceName));
  90:  }
  91:  }
  92:   
  93:  /// 
  94:  /// The Main method which polls for changes and
  95:  /// processes them.
  96:  /// 
  97:   
  98:  private void PollForChanges()
  99:  {
 100:  try
 101:  {
 102:  // Method which processes the changes
 103:  ProcessChanges();
 104:  }
 105:  catch(Exception e)
 106:  {
 107:  LogHelper.LogError(e.ToString());
 108:  }
 109:  finally
 110:  {
 111:  // Save data
 112:  }
 113:  }
 114:   
 115:   
These type of services have proved extremely useful for us over the past few years - it has allowed us to monitor changes, pull/push data - quite good for integrating diverse systems.

Hope you find this useful.

No comments:

Post a Comment