Trigger

Developing a custom trigger can be useful in scenarios where a task is not scheduled at a uniform time interval but rather based on the confluence of one or more
criteria that meet a business rule relevant to the organization. The development process for custom triggers is fairly easy and provides developers with ways to
test the functionality of the trigger before being plugged into the system. The custom trigger implementation derives from the Taskmatics.Scheduler.Core.Trigger
class. This class provides the following interface to the developer:

Initialization

All custom logic for a trigger is done in the constructor method of the trigger. The constructor has access to the Context property which contains the parameters that
are passed into the trigger (in the case of a custom configuration object). If using standard .NET configuration, accessing the System.Configuration.ConfigurationManager from the custom
trigger code is also supported. The following example shows the basic initialization of a trigger:

In this code segment, a custom configuration class named FolderSizeTriggerInputParameters (not shown) is used to represent the input parameters of the trigger.
Upon construction, the Context.Parameters class can be cast to that type and then accessed to set the private class members that will be used throughout the
remainder of the code. Custom configuration classes are linked to their target object by the InputParameters and OutputParameters attributes. For more detailed
information and examples on the use of custom configuration classes in the Scheduler API, see Custom Configuration in the Development Section.

The following example represents the same initialization scenario using conventional .NET configuration:

In this code segment, a custom configuration section called FolderSizeTriggerSection (not shown) is used to provide the parameter values for the trigger.

When a task is scheduled in the administration website, each trigger that was selected for that task is constructed, initializing the trigger. Any exceptions that are
thrown from the logic in the constructor will be caught by the system and will cause the trigger not to be loaded. A message will be shown to the user on the
Scheduled Task Detail screen indicating the error that caused the trigger not to load. Should a retry of the trigger initialization be desired, simply hit the
Save button on the Scheduled Task detail screen. The system will then reload all associated triggers.

Custom triggers should be tested thoroughly prior to being added to the system. The Taskmatics.Scheduler.Core.TriggerHarness class is provided to aid developers in the
testing of the trigger logic. See the Testing section below for more details about testing custom trigger implementations.

Firing the Trigger

In the Scheduler system, when a trigger is fired, the Fired event is raised on the Taskmatics.Scheduler.Core.Trigger class. This event is handled by the coordinator
and the task associated with that trigger is dispatched to an Agent for execution. In custom trigger implementations this is done by calling the OnFired method,
passing an optional object that represents parameters that the task can use to determine what caused the trigger to fire:

In the above example, a timer interval (not shown) elapses and the target directory's disk size is checked against the threshold value. If the size meets or exceeds
the threshold, the trigger is fired. A new object is created that takes in the actual size of the folder, which will be passed to the task so that the information
can be used by the logic of the task, if required. When executed, a Task will have access to information about the trigger that executed it, as well as the input
parameters and output parameters objects for that trigger.

The OnFired method can be overridden by the developer should there be a case where the functionality is to be augmented or additional logic surrounding the conditions
of firing the trigger are desired. The example below shows a modified version of the OnFired method in a custom trigger implementation.

Disposal

The Dispose method is where any resource that are created or maintained by the custom trigger can be cleaned up. It is recommended that Dispose be
overridden anytime that the custom trigger class contains members that hold system resources. Failure to do this can result in system instability over time as
the numerous trigger instances increase pressure on system memory capacity. When a scheduled task is disabled or removed from the system, the triggers associated
with that task are unloaded from the system. When being unloaded, the Dispose method is called on the trigger. Consider the following example:

public override void Dispose()
{
_timer.Stop();
_timer.Dispose();
}

In the above example, a System.Threading.Timer exists in the class that drives polling functionality. The dispose method allows the timer to be stopped and collected
to ensure that the timer elapsed handler will no longer fire.

Example Implementation

The following is a full example of a custom trigger. This trigger will execute a task whenever the used disk space of all files/folders in a specified folder meet
or exceed a specified size (in MB). This example demonstrates the use of custom input/output parameters:

When developing a task that is expected to use this trigger, the developer can access information about the trigger that fired the task as well as the input
and output parameters. For an example, consider this simple task definition:

The Context property of the task contains useful information related to the task, the trigger that was responsible for its execution and the event listeners configured
on it, as well as the parameters involved. More information about the Taskmatics.Scheduler.Core.TaskContext class and task development can be found in the
Task Development section.

Testing Custom Triggers

Because most of the functionality of the base Taskmatics.Scheduler.Core.Trigger class is internal, including the Fired event, it is recommended to use the
Taskmatics.Scheduler.Core.TriggerHarness<TTrigger> class to perform testing against a custom trigger implementation. The following example uses the TriggerHarness<TTrigger>
class to test the FolderSizeTrigger from the full example defined above:

In the above example, the Taskmatics.Scheduler.Core.TriggerHarness<TTrigger> class is instantiated for the FolderSizeTrigger generic parameter and a new instance of the FolderSizeTriggerInputParameters class is
passed in to simulate the configuration object that would be created in the administration website when configuring the trigger. Finally, an event handler is wired
into the TriggerFired event of the harness, which listens for the trigger to be fired. While simple, this demonstrates how easy it is to test and build unit test
cases around custom trigger implementations using the test harness class.

Note that in the above example, it would be just as easy to test a trigger that uses native .NET configuration by not passing anything to the Taskmatics.Scheduler.Core.TriggerHarness<TTrigger>
constructor. In this case, the host project would need to have either a Web.config or App.config file configured as needed by the custom trigger implementation.

For trigger implementations that have dependencies and require testability the Taskmatics.Scheduler.Core.TriggerHarness<TTrigger> constructor takes an optional factory delegate that returns an instance
of TTrigger. This allows for instantiation of the trigger with a custom constructor that will accept mock implementations of any dependencies for testing purposes. The following example demonstrates this use case:

The above example shows the usage of the factory delegate on the Taskmatics.Scheduler.Core.TriggerHarness<TTrigger> class. A secondary constructor is created on the FolderSizeTrigger class that takes in
the implementation of the IFolderSizeProvider dependency. The Scheduler system will always call the default constructor of Trigger when hosting new triggers in the system, so the secondary constructor allows
the unit test to pass a mock dependency used for testing. The Taskmatics.Scheduler.Core.TriggerHarness<TTrigger> is responsible for setting up the TriggerContext that will be used from within the harness when executing the custom trigger code,
so when using the factory delegate, it is important to construct the Trigger implementation inside the delegate and not used as a closure as this will affect the timing of context creation and result in an error when attempting to create/access the TriggerContext.