Click here to Skip to main content
Click here to Skip to main content

Windows Service State Publisher

, 13 May 2004
Rate this:
Please Sign up or sign in to vote.
Monitors Windows Services for changes in state using well known design patterns.

Introduction

ServiceStatePublisher extends the .NET System.ServiceProcess library. The ServiceStatePublisher library introduces the ability to observe or listen for state changes to a Windows Service.

Why create this library extension? To get experience using several design patterns. In particular, this project will incorporate 3 design patterns:

  1. Singleton
  2. State
  3. Observer/Publish-Subscribe

Having been a casual observer of this site for awhile, I know the design patterns topic is one of those religious hotbeds. For me, they represent a nice way to communicate what you're doing -- and if by chance someone recognizes the design pattern...you know, "hey, that's the fuzzy duck pattern"...then all the better. I don't take it as criticism or superiority, but rather as a compliment. The observer was excited to recognize something they learned can be used in practice. And, the code was clear enough for them to see the forest through the trees (i.e. beneath the surface of the code). Hopefully, this code will be clear enough. Wink | ;-)

To demonstrate the use of this library, I have constructed the Oracle Service Manager application, found elsewhere on this site. Oracle Service Manager mimics the Microsoft SQL Server Service Manager.

Background

I have listed the references, at the end of this article, from which I derived my work; however, there are many articles on this site, and the Internet, that are excellent sources for the topics covered. Topics covered include the design patterns identified above as well as .NET Framework topics such as the System.ServiceProcess .NET namespace, delegates, and events.

Problem Analysis

Windows Services are not designed to fire events alerting observers when a service has changed state. This library extension provides a framework to add one or more service observers. For example, if you wanted to create a utility to monitor and control the Oracle Windows services – just like the SQL Server Service Manager – you would need 3 observers: a dialog observer, a tray icon observer, and a timer/polling observer. A change in the state of the server ought to be broadcasted to all listeners, so they can decide if there is any action to take – like updating a user interface.

I have created a project and wrote an article, found elsewhere on CodeProject, for just this purpose – monitoring and controlling Oracle services – it’s called Oracle Service Manager.

Using Design Patterns

There are two primary issues to solve. The first is the state of the service. The operations performed on a service (e.g. Start, Stop, Pause, and Continue) are dependent on the internal state of the service. In other words, a running service can be stopped and, possibly, paused; however it cannot be started or continued. Most of the time, this issue is solved with a big conditional statement. The State pattern puts each branch of the conditional statement in a separate class. [GHJV98]

The second issue concerns notifying interested parties of the change in state. When a running service stops, all parties interested in observing the service should be notified of the new state regardless of whether that party initiated the request. The Observer pattern allows us to notify other objects without making assumptions about who these objects are. In other words, you don't want these objects tightly coupled. [GHJV98]

Gamma et al, classify both patterns as behavioral. In addition, the two patterns have a relationship to one another in this library. That is, examining the two patterns reveals, the observer pattern means nothing unless the state of a service changes. This implies that the observer pattern has a dependency on the state pattern. Therefore, the observer pattern implementation needs to subclass the state pattern implementation. In short, the observer subject will derive from, or extend, the state context class.

The state pattern declares each state be represented by a concrete class. This introduces a decision of when to allocate and clean up the concrete state objects. Since we are only interested in tracking one service at a time, and we have a limited number of concrete states, we can minimize memory allocations by creating all concrete state objects at startup. This introduces a requirement to ensure and manage a single instance. This problem is solved by a third pattern – the Singleton pattern [GHJV98].

Below, I discuss the characteristics of the design pattern implementations. If you are only interested in using this library, your interest will lie in one of two classes: the ServiceContext in the State pattern; or the ServiceSubject in the Observer pattern.

State Pattern

Figure 1.  Diagram of State Pattern as illustrated in Design Patterns [GHJV98]

The State pattern diagrammed above outlines the participants needed to support the model. The equivalent types needed for the ServiceStatePublisher library are:

Context ServiceContext.

  • Defines the interface of interest to clients. In other words, as a user of this pattern, this is the class to program against. This class exposes all supported behaviors and properties encapsulated by the ConcreteState classes.
  • Maintains an instance of a ConcreteState subclass that defines the current state†
  • Context delegates state-specific requests to the current ConcreteState object†
  • Context passes itself as an argument to the State object handling the request. This lets the State object access the context†
  • Either Context or the ConcreteState subclasses can decide which state succeeds another and under what circumstances†
public class ServiceContext
{
    protected ServiceController _controller;
    protected ServiceState _state;

    public ServiceContext()
    {
        State = ServiceStateUnknown.Instance;
        Controller = null;
    }

    public void Start()
    {
        State.Start( this);
    }

    public void Stop()
    {
        State.Stop( this);
    }

    public void Pause()
    {
        State.Pause( this);
    }

    public void Continue()
    {
        State.Continue( this);
    }
}

State ServiceState.

  • Defines an interface for encapsulating the behavior associated with a particular state of the Context
  • I did not implement this as an interface because the actual implementation has some important, but not obvious, default behavior. So, instead, I used a public abstract class to indicate this is a base class from which subclasses derive. More importantly, this class cannot be instantiated on its own. Indeed, this class is the base class for the ConcreteState classes discussed below.
  • As a base class, ServiceState is the natural place to put common error logging for each of the subclasses.
  • The important default behavior is the empty body for each virtual function. This means, by default, a call to a method that is not overridden will do nothing. This will ease the burden for each ConcreteState class.
public abstract class ServiceState
{
    public virtual void Start( ServiceContext context) { }
    public virtual void Start( ServiceContext context, string[] args) { }
    public virtual void Stop( ServiceContext context) { }
    public virtual void Pause( ServiceContext context) { }
    public virtual void Continue( ServiceContext context) { }

    …

    protected virtual void LogError( string message, EventLogEntryType entryType)
    {
        if (!EventLog.SourceExists( SSP_SOURCE))
            EventLog.CreateEventSource( SSP_SOURCE, SSP_EVENTLOG);

        EventLog sspLog = new EventLog();
        sspLog.Source = SSP_SOURCE;

        sspLog.WriteEntry( message, entryType);

        sspLog.Close();
    }
}
ConcreteStates ServiceRunning, ServiceStopped, ServicePaused,
ServicePendingStart, ServicePendingStop, ServicePendingPause,
ServicePendingContinue, ServiceStateUnknown
  • Each subclass implements a behavior associated with a state of the Context
  • Each ConcreteState class is derived from ServiceState.
  • Each ConreteState has a one-to-one mapping with the ServiceControllerStatuses in .NET, with one exception.
  • Included is a class to reflect an unknown state, such as when a server or service has not been selected.
  • Because the base class, ServiceState, implements a default behavior, only those methods requiring overrides must be implemented. In other words, the ServiceRunning state need only override the methods that are allowable in that state – Stop() and Pause(). If the client program calls Start() or Continue() when the state is ServiceRunning, the default behavior is executed – do nothing. This treatment means we do not need to explicitly check for invalid requests, because the invalid request will be dealt with gracefully without error.
public class ServiceRunning : ServiceState
{
    …

    public override void Stop( ServiceContext context)
    {
        try
        {
            context.Controller.Stop();
            context.Controller.WaitForStatus( 
                ServiceControllerStatus.Stopped, new TimeSpan(0,0,30));
            ChangeState( context, ServiceStopped.Instance);
        }
        catch (TimeoutException te)
        {
            LogError( te.Message, EventLogEntryType.Warning);
            throw;
        }
    }

    public override void Pause( ServiceContext context)
    {
        try
            {
            context.Controller.Pause();
            context.Controller.WaitForStatus( 
                ServiceControllerStatus.Paused, new TimeSpan(0,0,30));
            ChangeState( context, ServicePaused.Instance);
        }
        catch (TimeoutException te)
        {
            LogError( te.Message, EventLogEntryType.Warning);
            throw;
        }
    }
}

Observer Pattern

Figure 2.  Diagram of Observer Pattern as illustrated in Design Patterns [GHJV98]

The Observer pattern diagrammed above outlines the participants needed to support the model. The Observer pattern is also known as: Listener and Publish-and-Subscribe. One noteworthy item is how well .NET provides native support for this pattern through built-in functionality found in delegates and events. The equivalent types needed for the ServiceStatePublisher library are:

Subject Not applicable.

  • Knows its observers. Any number of Observer objects may observe a subject
  • Provides an interface for attaching and detaching Observer objects§ (e.g., in C#, the syntax to attach is object.event += new EventHandler(); to detach, C# uses the -= operator)
  • .NET provides this functionality through its built-in support of delegates and events
  • The ConcreteSubject establishes this contract through .NET’s event support – see below.

ConcreteSubject ServiceSubject.

  • This is the publisher and defines the interface of interest to clients'. In other words, if you want published events, this is the class you should use, or program against, in your application.
  • Stores state of interest to ConcreteObserver objects§
  • Sends a notification to its observers when its state changes§ (i.e., publishes the event to subscribers)
  • .NET supports this functionality through its built-in support of delegates and events
public class ServiceSubject : ServiceContext
{
    public event EventHandler ServerChanged;
    public event EventHandler ServiceChanged;
    public event EventHandler StateChanged;

    protected string _serverName;

    public ServiceSubject() : base() { }

    public ServiceSubject( ServiceController service)
    {
        Controller = service;
    }

    public virtual string ServerName
    {
        get { return _serverName; }
        set
        {
            _serverName = value;
            OnServerChanged( EventArgs.Empty);
            if (Controller != null && _serverName != Controller.MachineName)
                Controller = null;
        }
    }

    public override ServiceController Controller
    {
        get { return _controller; }
        set
        {
            _controller = value;
            _state = QueryServiceState();
            OnServiceChanged( EventArgs.Empty);
        }
    }

    public override ServiceState State
    {
        get { return _state; }
        set
        {
            _state = value;
            OnStateChanged( EventArgs.Empty);
        }
    }

    public virtual void RefreshServices()
    {
        OnServerChanged( EventArgs.Empty);
    }

    protected virtual void OnServerChanged( System.EventArgs e)
    {
        if (ServerChanged != null)
            ServerChanged( this, e);
    }

    protected virtual void OnServiceChanged( System.EventArgs e)
    {
        if (ServiceChanged != null)
            ServiceChanged( this, e);
    }

    protected virtual void OnStateChanged( System.EventArgs e)
    {
        if (StateChanged != null)
            StateChanged( this, e);
    }
}

Observer Not applicable.

  • Defines an updating interface for objects that should be notified of changes in a subject§
  • .NET provides this functionality through its built-in support of delegates and events

ConcreteObservers Implemented in client application.

  • This is the actual subscriber or listener
  • Maintains a reference to a ConcreteSubject object§
  • Stores state that should stay consistent with the subject's§
  • Implements the Observer updating interface to keep its state consistent with the subject's§
  • .NET provides this functionality through its built-in support of delegates and events
  • The observers are implemented in the client application. I created an example, Oracle Service Manager, for some “real” world applicability.
using System;
using System.ServiceProcess;
using kae.ServiceStatePublisher;

public class SampleObserver
{
    public SampleObserver( ServiceSubject subject)
    {
        if (subject != null)
        {
            subject.ServerChanged += new EventHandler( this.OnServerChanged);
            subject.ServiceChanged += new EventHandler( this.OnServiceChanged);
            subject.StateChanged += new EventHandler( this.OnStateChanged);
        }
    }

    private void OnServerChanged( object sender, EventArgs e)
    {
        ServiceSubject subject = (ServiceSubject) sender;
        …
    }

    private void OnServiceChanged( object sender, EventArgs e)
    {
        ServiceSubject subject = (ServiceSubject) sender;
        …
    }

    private void OnStateChanged( object sender, EventArgs e)
    {
        ServiceSubject subject = (ServiceSubject) sender;
        …
    }
}

Singleton Pattern

Figure 3.  Diagram of Singleton Pattern as illustrated in Design Patterns [GHJV98]

The Singleton pattern diagrammed above outlines the sole participant needed to support the model. The Singleton pattern is applied differently depending on whether you need a single instance inside a single process or across multiple processes. In the latter case, an operating system level resource must be used. In the former case, a static constructor can be used to create the one and only instance.

Singleton ServiceRunning, ServiceStopped, ServicePaused
ServicePendingStart, ServicePendingStop, ServicePendingPause
ServicePendingContinue, ServiceStateUnknown
  • Defines an Instance operation that lets clients access its unique instance. Instance is a class operation**
  • Responsible for creating its own unique instance**
  • In C#, we use a static property as the accessor
public class ServiceRunning : ServiceState
{
    private static ServiceRunning _instance;

    static ServiceRunning()
    {
        lock (typeof(ServiceRunning))
        {
            if (_instance == null)
                _instance = new ServiceRunning();
        }
    }

    public static ServiceRunning Instance
    {
        get { return _instance; }
    }

    …
}

Future Directions

As I have mentioned above, the ServiceStatePublisher library is used in a companion article, Oracle Service Manager, as an example application that may have some relevance to your development environment. In addition, using this library for a Visual Studio add-in to manage the Windows Services of any database (e.g., Oracle, DB2, MySQL, SQL Server, etc.) could be a nice alternative to the browsing mechanism offered by the Server Explorer in Visual Studio. Perhaps, there is also an enhancement to ensure changes in state happen in a worker thread, allowing the main thread to be more responsive to the interface.

Special Thanks

I wish to thank Liam Corner for lending me his expertise and review. He provided viewpoints I hadn’t previously considered. As a result, this project and future projects will be better for it.

Footnotes

  • Participant attributes and collaborations for State pattern [p.305] detailed in [GHJV98].
  • § Participant attributes and collaborations for Observer pattern [p.293] detailed in [GHJV98].
  • ** Participant attributes and collaborations for Singleton pattern [p.127] detailed in [GHJV98].

References

History

  • 14 May 2004 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Rob Kayman

United States United States
Dwight D. Eisenhower wrote: "In battle, I believe plans are useless; however, planning is indespensable."
 
Hmm, perhaps he was an early adopter of agile beliefs?! Wink | ;-)

Comments and Discussions

 
GeneralIntegrate window service with any web application [modified] PinmemberFaheem Habib25-Aug-05 1:39 
GeneralGetting an Object out of a windows service Pinmembermark_in_TV25-Oct-04 8:41 
GeneralRe: Getting an Object out of a windows service Pinmemberkryzchek11-Jan-05 4:51 
GeneralRe: Getting an Object out of a windows service Pinmembercosmicb2-Aug-05 4:26 
GeneralPattern Diagrams PinmemberKent Boogaart20-May-04 17:21 
GeneralRe: Pattern Diagrams PinmemberRob Kayman21-May-04 16:46 
Generalfwiw: more patterns PinsussPer Søderlind24-May-04 13:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 14 May 2004
Article Copyright 2004 by Rob Kayman
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid