Last time we saw how an implementation of a server over the notification server framework looks like. We discussed code of events, logics and rules. Something we have not discussed yet is how do we link components to one another. This is something that is resolved via a mechanism called component builder.

The component builder is an interface provided by the notification server framework, where one defines the components of a specific server and defines the links between these components. It consists of a single method:

1
2
3
4
public interface IComponentBuilder
{
    void BuildComponents(IRuntimeContext context);
}

The interface IRuntimeContext is an interface which provides information about our server. It will be discussed in another post. IRuntimeContext has a property named Components which is of type IComponentContainer. IComponentContainer is, as its name suggests, a container of the server components, and it extends (inherits from) the interface ICollection<IComponent>. I don’t want to go into details about the interfaces IComponent and IComponentContainer, so for now let’s just assume that all components implement the interface IComponent.

In the method BuildComponents we are expected to create the components of our server and link them one another. Let us demonstrate using the fractal processing server sample we discussed last time:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class FractalProcessingServerComponentBuilder : IComponentBuilder
{
    public void BuildComponents(IRuntimeContext context)
    {
        // Create components
        RequestListener requestListener = new RequestListener();
        PixelGeneratorLogic pixelGeneratorLogic = new PixelGeneratorLogic();
        PixelToComplexConvertLogic pixelToComplexConvertLogic = new PixelToComplexConvertLogic();
        MandelbrotLogic mandelbrotLogic = new MandelbrotLogic();
        JuliaLogic juliaLogic = new JuliaLogic();
        ColorConvertLogic colorConvertLogic = new ColorConvertLogic();
        PixelStoreLogic pixelStoreLogic = new PixelStoreLogic();
        BitmapConvertLogic bitmapConvertLogic = new BitmapConvertLogic();
        FileSystemDispatcher fileSystemDispatcher = new FileSystemDispatcher();

        // Add components to component container
        IComponentContainer componentContainer = context.Components;
        componentContainer.Add(requestListener);
        componentContainer.Add(pixelGeneratorLogic);
        componentContainer.Add(pixelToComplexConvertLogic);
        componentContainer.Add(mandelbrotLogic);
        componentContainer.Add(juliaLogic);
        componentContainer.Add(colorConvertLogic);
        componentContainer.Add(pixelStoreLogic);
        componentContainer.Add(bitmapConvertLogic);
        componentContainer.Add(fileSystemDispatcher);

        // Link components
        // Lower chain:
        requestListener.Subscribe(pixelStoreLogic, new ActivateAllRule());
        colorConvertLogic.Subscribe(pixelStoreLogic, new ActivateAllRule());
        pixelStoreLogic.Subscribe(bitmapConvertLogic, new ActivateAllRule());
        bitmapConvertLogic.Subscribe(fileSystemDispatcher, new ActivateAllRule());

        // Upper chain
        requestListener.Subscribe(pixelGeneratorLogic, new ActivateAllRule());
        pixelGeneratorLogic.Subscribe(pixelToComplexConvertLogic, new ActivateAllRule());
        pixelToComplexConvertLogic.Subscribe(mandelbrotLogic, new MandelbrotRule());
        pixelToComplexConvertLogic.Subscribe(juliaLogic, new JuilaRule());
        mandelbrotLogic.Subscribe(colorConvertLogic, new ActivateAllRule());
        juliaLogic.Subscribe(colorConvertLogic, new ActivateAllRule());
    }
}

As we can see, we first create an instance of each component of our server, then add all the component instances to the Components property of the provided IRuntimeContext. Then we perform the links. We perform the links by calling a method called Subscribe. Every component which is not a target component (i.e., it allows other components to be linked to it) implements an interface called IPublisher which contains a method with the following signature:

1
2
3
4
5
public interface IPublisher
{
    void Subscribe(IRecipient recipient, IRule rule);
    // ...
}

Again, I do not wish to discuss the interface IRecipient right now, but every component which is not a source component (i.e., it allows other components to link to it) implements IRecipient. The Subscribe method tells the publisher we are interested in receiving events that pass the given rule. This is always confusing for new developers, as they expect Subscribe to be a method of the subscriber, but this is the convention, and it is the same in other frameworks, such as Reactive Extensions.

Note that when we subscribe our instances of MandelbrotLogic and JuliaLogic to our PixelToComplexConvertLogic, we provide instances of the rules we implemented last time. For the rest of the components, we provide an instance of ActivateAllRule, that is an implementation of IRule that allows every event to pass: it always returns true from the IsActivated method.

A few remarks about the IComponentBuilder interface:

  • In the far past, there was no interface, but a class named ComponentBuilder with 3 methods for building components BuildListeners, BuildLogics, BuildDispatchers, in which one would build components of certain types. There was probably also a method called LinkComponents where one would link the server’s components.
  • In modern versions of the notification server one does not need to implement the interface IComponentBuilder. Instead, the framework’s user defines their components and their links in a visual tool. This definition is saved as a configuration file. Then the built-in IComponentBuilder implementation reads this file and constructs the components and links them accordingly.