Extending Task Planner
All components of a task can be implemented yourself. The components are triggers, jobs, result actions and series.
In this guide any trigger, job, result handler, or series is called a 'component' because many explanations and relations are the same for all components.
To add new components, a plugin is required which has the taskplanner
plugin as a dependency. See Plugins Guide for general help on how to create an own plugin.
For each component, you must implement a factory class which provides all required information and a real implementation of the component.
Life cycle
When defining a new task, either by using the Task Planner web application or the public API, then that task will consist of component definitions. The definitions only contain configured settings or other meta information, but no functionality or program logic.
Example: The TriggerDefinition for the Time Trigger contains the execution time and the repeat interval.
For each kind of component, such as the time trigger and email result action, a factory class exists and is registered in ServerPluginManager. The factory is responsible to create, validate, and provide information about the component.
Example: The TimeTriggerFactory for the Time Trigger specifies which options can be set in the web application, what information is displayed, and will create a TimeTrigger implementation.
The factory produces the real implementation of the component, like a real job which does something when executed.
Example: The TimeTrigger contains the program logic which will start tasks of the Task Planner according to the settings configured in the definition.
In short form: Definition → Factory → Implementation
The moment when a real implementation is constructed with the factory depends on the component: Jobs, result actions, and series are created when the task is executed and a trigger is created when the task is activated.
When adding a new kind of component, you must implement a new factory and a new component implementation, but no new definition because the definitions are all the same for one component type.
Example: All triggers are defined with a TriggerDefinition
, but each kind of trigger has its own TriggerFactory
and Trigger
implementation class.
Component Keys
A kind of component is identified with a unique key, e.g. 'trigger.time' for the time trigger. The key is called EXTENSION_NAME. The factory which is responsible to create the implementation must know the key for that component. Predefined Factory classes expose a public constant EXTENSION_NAME which can be helpful when using the public API.
The definition of a component also contains that key so the corresponding factory can be located. Additionally it can (optionally) contain some properties which typically affect the implementation behavior, e.g. the repeat interval of the time trigger.
Component Factory
The main objective of the factory is to construct an implementation for the definition which has been set up by using the Task Planner web application or the public API. For example the TriggerFactory.createInstanceFrom()
creates a Trigger object from a TriggerDefinition
.
Other methods of the factory provide information about possible fields and settings of the component. If you do not want to use the Task Planner web application, then some methods of the factory can be left empty.
Method summary:
-
validate(definition, taskID)
- here the settings can be validated. This can be left empty if no validation is required or desired. -
getSummary(definition)
- provides a human readable summary of the configured component which is used in the web application. You can return null if you do not use the web application. -
createInstanceFrom( definition )
- here the actual component is constructed. This is the core method that must be implemented. -
getInformation(taskID)
- this provides information about the properties of the component and some display values for the web application. The fields defined here are also responsible to replace the placeholders with a value from the series if one is configured. If not using the web application, then you can most likely simply return null here, however keep in mind that the replacement of placeholders in a series will not work then.
There are some more methods which can be overridden but they are usually not required. See the documentation of the factory classes and its methods for more details.
Factory and Implementation classes
Which classes can be extended and what methods do they expose?
The factory base class is also the extension point for registering it in ServerPluginManager.
Example: spm.register( TriggerFactory.class, new TimeTriggerFactory() );
Trigger
To add a new kind of trigger you must extend com.inet.taskplanner.server.api.trigger.TriggerFactory
and implement your trigger with the com.inet.taskplanner.server.api.trigger.Trigger
interface.
Job
To add a new kind of job you must extend com.inet.taskplanner.server.api.job.JobFactory
and implement your job with the com.inet.taskplanner.server.api.job.Job
interface.
Additional factory methods
-
getResultFlavors(definition)
- returns which ResultFlavor(s) this job will generate with the given settings. An empty list can also be returned if your job does not produce results. -
validateCondition(definition)
- here the condition settings can be validated if you use conditions.
Result actions
To add a new kind of result action you must extend com.inet.taskplanner.server.api.action.ResultActionFactory
and implement your result action with the com.inet.taskplanner.server.api.action.ResultAction
class.
Additional factory methods
-
getSupportedFlavors(definition)
- returns which ResultFlavor(s) this result action is able to process with the given settings. If your action does not require any results thenResultFlavor.NONE
should be returned.
Series
To add a new kind of series you must extend com.inet.taskplanner.server.api.series.SeriesFactory
and implement your series with the com.inet.taskplanner.server.api.series.Series
interface.
How to start
The recommended way how to add a new component:
-
Create a plugin which has
taskplanner
as a dependency -
Create a factory class which extends the factory class for your component type (see above).
-
Create a String constant for the KEY / EXTENSION_NAME and use that in your factory constructor.
-
If your component has settings, then add public constants for the property keys of your settings. Optionally implement the
validate
method so it checks your settings for correctness. -
If your component will be used in the web application, then the methods
getSummary()
andgetInformation()
should be implemented, otherwise can be left to return null. -
If you want to use a series with your component, your component is a
Job
orResultAction
, and you are going to want to support placeholders in some settings of your component, then you must also implement thegetInformation()
method. For the placeholders, it is important that each property where you want placeholders to work must have a corresponding field representation (same property name) in theResultActionInfo
orJobInfo
. -
Create the implementation class, see above. Let the
createInstanceFrom()
method create your implementation component and let it transfer the settings from your definition to your implementation class. -
Register your factory in ServerPluginManager
Example
Factory class
public class DTXActionFactory extends ResultActionFactory<ResultAction> { public static final String EXTENSION_NAME = "DTX Action"; public static final ResultFlavor DTX = ResultFlavor.create( "DTX" ); //some custom flavor, you need some custom job which creates results of this type public static final String PROPERTY_SYSTEM = "dtx system"; public DTXActionFactory() { super( EXTENSION_NAME ); } /** * {@inheritDoc} */ @Override public List<ResultFlavor> getSupportedFlavors( ResultActionDefinition definition ) { List<ResultFlavor> list = new ArrayList<>(); list.add( DTX ); return list; } /** * {@inheritDoc} */ @Override public ResultActionInfo getInformation( GUID taskID ) { //can return null if you do NOT use the web application AND you do not want to use placeholders in this result action // return null; List<Field> fields = new ArrayList<>(); //this is required if you want to use series and placeholders in PROPERTY_SYSTEM must be replaced fields.add( new TextField( PROPERTY_SYSTEM, "DTX System Host" ) ); URL iconUrl = getClass().getResource( "path/to/your/icon/in/src/of/your/plugin" ); return new ResultActionInfo( EXTENSION_NAME, "Send to DTX System", "Description of the Action", iconUrl, null, fields ); } /** * {@inheritDoc} */ @Override protected void validate( ResultActionDefinition definition, GUID taskID ) throws ValidationException { // can always be left empty String system = definition.getProperty( PROPERTY_SYSTEM ); if( system == null ) { throw new ValidationException( "DTX System must be specified!!" ); } } /** * {@inheritDoc} */ @Override protected ResultAction createInstanceFrom( ResultActionDefinition definition ) { return new DTXResultAction( definition.getProperty( PROPERTY_SYSTEM ) ); } /** * {@inheritDoc} */ @Override public SummaryInfo getSummary( ResultActionDefinition definition ) { //can return null if you do NOT use web gui List<SummaryEntry> entries = new ArrayList<>(); entries.add( new SummaryEntry( "Transmit to DXT System", definition.getProperty( PROPERTY_SYSTEM ) ) ); return new SummaryInfo( entries ); } }
Result action class
public class DTXResultAction extends ResultAction { private String dtxSystem; public DTXResultAction( String dtxSystem ) { this.dtxSystem = dtxSystem; } /** * {@inheritDoc} */ @Override protected void handle( List<JobResultContainer> results ) throws TaskExecutionException { //we only process DTX results results.forEach( ( resultContainer ) -> { List<Result> dtxResults = resultContainer.getResults( DTXActionFactory.DTX ); //... do send somewhere to dtxSystem ... } ); } }
In your Plugin class
@Override public void registerExtension( ServerPluginManager spm ) { spm.register( ResultActionFactory.class, new DTXActionFactory() ); //... }