This is the second in a series of posts about Siphon, a set of data monitoring utilities for .NET under the MIT license. The source code can be found on GitHub.
Introduction
In this article, I’m going to cover how Siphon configuration works, the options and how to use the configuration classes to read/write configuration information.
Configuration Section
Here’s a sample Siphon configuration section from the source code:
<configSections>
<section name=“siphon” type=“ChrisLaco.Siphon.SiphonConfigurationSection, Siphon” />
</configSections><siphon>
<monitors>
<monitor name=“IntervalMonitor” type=“ChrisLaco.Siphon.LocalDirectoryMonitor, Siphon”>
<settings>
<add name=“Path” value=“C:\” />
<add name=“Filter” value=“*.tmp” />
</settings>
<schedule type=“ChrisLaco.Siphon.IntervalSchedule, Siphon”>
<interval value=“1.2:3:4” />
</schedule>
<processor type=“ChrisLaco.Siphon.DummyProcessor, Siphon” />
</monitor>
</monitors>
</siphon>
Let’s break this down piece by piece. First, we need to tell .NET who’s in charge of the siphon configuration section:
<configSections>
<section name=“siphon” type=“ChrisLaco.Siphon.SiphonConfigurationSection, Siphon” />
</configSections>
Next, we add a new monitor element to the monitors collection:
<monitor name=“IntervalMonitor” type=“ChrisLaco.Siphon.LocalDirectoryMonitor, Siphon”>
Here we have a monitor named IntervalMonitor and told Siphon what type to load. If you’re not familiar with using type strings in .NET configuration, the string above tells .NET that we want to use the class ChrisLaco.Siphon.LocalDirectoryMonitor in the Siphon assembly. Note that if you want to use types that are installed into the GAC you will need to use the full type string including the Culture, PublicKeyToken and Version.
Next, we configure setting required by the LocalDirectoryMonitor:
<settings>
<add name=“Path” value=“C:\” />
<add name=“Filter” value=“*.tmp” />
</settings>
The settings consist of name/value pairs that are passed to the monitors Initialize() method. The number of options and their values vary depending on the monitor being used. Next, we’ll tell the monitor how often to look for new data:
<schedule type=“ChrisLaco.Siphon.IntervalSchedule, Siphon”>
<interval value=“1.2:3:4” />
</schedule>
Just like with the monitor element above, we need to tell Siphon what schedule class we want to use. In our case, we’re going to use the IntervalSchedule, and set the interval using a string TimeSpan.Parse() understands: 1 day, 2 hours, 3 minutes and 4 seconds. If you are interested in a daily schedule, you can also use the daily element to specify a daily schedule:
<schedule type=“ChrisLaco.Siphon.DailySchedule, Siphon”>
<daily>
<time value=“8:00” />
<time value=“15:00” />
</daily>
</schedule>
Note: whether you use an interval or a daily schedule, the schedule type you use must understand the configuration you’ve chosen. As we’ll see in the Configuration classes later, the configuration format is setup to handle the most common use cases, like daily schedule values. Just like the monitor element, the schedule element also has a settings element used to pass name/value options to the specified schedule type.
Now that we’ve chosen a monitor and a schedule, the last thing to do is to tell Siphon who will process the newly found data as it arrives:
<processor type=“ChrisLaco.Siphon.DummyProcessor, Siphon” />
Here we’ve loaded the DummyProcessor, which does does nothing with new data and always returns True. As with the monitor and schedule elements, the processor element also has a settings element collection.
Reading Configuration
Siphon comes with a set of classes to read/write siphon configuration data. Once you have finished setting up your app.config, you can use the SiphonConfigurationSection class to load the section and loop through all of the configured monitors:
REM Load config section: defaults to ‘siphon’
Dim section As SiphonConfigurationSection = SiphonConfigurationSection.GetSectionREM Loop through monitor elements
For Each monitor As MonitorElement In section.MonitorsREM Get a setting
Dim path As String = monitor.Settings(“Path”).ValueREM create an instance of the specified type
Dim instance As IDataMonitor = monitor.CreateInstance
instance.Path = pathREM Set the schedule
instance.Schedule = monitor.Schedule.CreateInstanceREM Set the processor
instance.Processor = monitor.Processor.CreateInstanceREM Process new data
instance.Process()
Next
The code above is pretty straight forward. One item of interest: when CreateInstance is called, it creates an instance of the specified type and calls the Initialize() method on the new instance. While we set the Path setting manually, the monitors Initialize() could have also done the same thing.
All monitors must implement the IDataMonitor interface. All schedules must implement the IMonitorSchedule interface and all processors must implement the IDataProcessor interface. CreateInstance() looks for a constructor with no parameters in the type being created.
Writing Configuration
You can also use the configuration classes to programaticaly create configuration files. SImply create the settings in code, then save the config to the specified file:
Dim config As Configuration = ConfigurationManager.OpenExeConfiguration(“C:\Path\To\SomeApp.exe”)
Dim section As New SiphonConfigurationSection
Dim monitor As New MonitorElement(“TestMonitor”, “LocalDirectoryMonitor, Siphon”)monitor.Settings.Add(New NameValueConfigurationElement(“Path”, “C:\”))
monitor.Processor = New ProcessorElement(“MockProcessor, SiphonTests”)
monitor.Processor.Settings.Add(New NameValueConfigurationElement(“Foo”, “Bar”))
monitor.Schedule.Type = “IntervalSchedule, Siphon”
monitor.Schedule.Daily.Add(New TimeElement(New TimeSpan(100)))
monitor.Schedule.Daily.Add(New TimeSpan(200))
section.Monitors.Add(monitor)config.Sections.Add(“siphon”, section)
config.Save()
This will update the app.config file for the specified executable path with the new configuration information. The code above will generate something like this:
<?xml version=“1.0” encoding=“utf-8”?>
<configuration>
<configSections>
<section name=“siphon” type=“ChrisLaco.Siphon.SiphonConfigurationSection, Siphon, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0576062738dd7ad9” />
</configSections>
<siphon>
<monitors>
<monitor name=“TestMonitor” type=“LocalDirectoryMonitor, Siphon”>
<schedule type=“IntervalSchedule, Siphon”>
<interval value=“00:01:00” />
<daily>
<time value=“00:00:00.0000100” />
<time value=“00:00:00.0000200” />
</daily>
</schedule>
<processor type=“MockProcessor, SiphonTests”>
<settings>
<add name=“Foo” value=“Bar” />
</settings>
</processor>
<settings>
<add name=“Path” value=“C:\” />
</settings>
</monitor>
</monitors>
</siphon>
</configuration>