Requirements Scheduled tasks (ST)
At the moment the ST’s available in Railo aren’t powerful enough yet. The solution that we will implement for managing ST’s is required to run as an independent application (as well). The following requirements have been gathered so far:
General
The overview of the tasks should provide a list of tasks in different colors and with different additional information so that at first glance the most important things can be noticed.
General requirements
-
ST’s can have pre conditions which need to be true in order for the tag to be executed, for example:
- Current CPU % below a certain threshold
- Current Memory available larger than a threshold
-
After the ST has been executed, for example:
- Result must by in Json Format
- Send a confirmation or error Mail
- If the condition does not match, the task can be re executed several times within a certain interval
- A ST can have several states that depends on the last execution. You see if the Task was successful executed (precondition, postcondition, execute) and the error message.
- If a ST is executed atm, you see information to the current execution.
- The Log can be defined individually
-
ST’s can be grouped and virtually be executed as one ST
- The same options apply than for a single ST
- You can select whether to execute the ST’s within the group either sequentially or parallel, the Task in the group are executed seriell.
-
The execution plan of a ST should be more flexible. Some examples:
- Daily to different times
- On a certain day to a certain time
- After another ST has been completed
- Every 2nd day every week/month/year
- Every 2nd workday every week/month/year
- Recurring completely individual
- Define a priority for a ST (low, normal, high)
- An option that defines whether a scheduled task can only run once at the same time
Overview page
After starting the application, an overview page is displayed, that displays all Groups in alphabetical order. The Groups are sortable by any column. The columns of the Groups are:
- Name of the Group
- Count of ST in the Group
- Last execution
- Next execution (The title contains the type of the execution plan)
- Status
- Number of retries after a failed execution
Next to each Group is an edit button, that allows changing the details of the corresponding Group and al ST in the Group. Not running Groups can be started manually. If a Group is running, an additional abort button allows to kill the execution of the Group.
You can create a new empty Group at the Bottom of the Page, after defining the name of the Group and confirm with “next” you get on the “Create new Group Page”.
Detail page Group
You can change all properties of a Group in the detail page of the Group. The following fields can be created/modified:
- Name of the Group
- Logging for the Group
- Execution Plan (When the Group is executed, the interval and how many Railo tries to reexecute if it fails ).
Conditions (Pre|Post)
Below the group definition you can see a list of global pre and post conditions you can define for the group, which must be true in order for the group to get executed (pre condition) or and marked as successfully executed (post condition). More about defining conditions later.
Scheduled Tasks
In addition you can see a list of ST assigned to this group. The list shows the following information:
- Name of the Task
- Task Type (onMouseOver shows description)
- Status of the last execution
Next to each Task there is an edit button that allows changing the details of the corresponding ST.
Below this there are some input elements that allow you to create new ST’s by selecting a task type from the drop-down (URL-Task, CFC-Task …), define a name and click “create”.
Detail page ST:
You can change all properties of a ST in the detail form. The available fields and condition depend on the type of task used.
The following fields exist for all task types
- Name of the ST
- Logging, a task specific log defintion
In addition you can define task type specific (pre|post) conditions.
Conditions
Like written before it will be possible to define pre and post conditions for tasks and groups. Task conditions are specific to a task type and group conditions are the same for all groups.
All conditions allow defining one setting specific to the condition, like for example a weekday definition for a “weekday” pre condition.
Customization
At the moment Railo supports only URL based ST’s. The downside of URL based ST’s is as well that they always have to be invoked over CFHTTP which means that they leave the server and eventually call the local server again. This doesn’t make sense for local ST’s since it adds additional overhead to a call. And an abort of such a ST is only possible with additional effort.
The idea is, that the new ST implementation will support different types of Task Types. Like for instance:
- URL based Tasks
- CFC Based Tasks (local)
- ...
So the new way of defining scheduled tasks will be like defining a data source or cache. There will be a CFC that implements an interface that contains several functions to support ST calls. This means we are completely free in how exactly a task is working and what the task is doing. This is very similar to the gateway implementation.
If the ST implementation of a different engine will propose a complete different functionality, we can implement another CFC that mimics that behavior.
Here the interface that a task type must implement:
interface {
/** return the label of the Scheduled Task */
public string getLabel();
/** return the description of the Scheduled Task */
public string getDescription();
/** return the fields for this ST (see also Cache Driver and Gateway Driver),
Railo creates a Form with help of this field, the data populated by the client
are then provided to the init method */
public Field[] function fields();
/** called with the settings made in the form based on the given fields */
public void function onBeforeUpdate(struct data);// throws Exception;
/** executed the task
* @param data data populated by the client with help of the Fields.
* @return return true when the task was successfull executed */
public boolean function execute(struct data);
/**
* @return return the pre condition bundled with this ST Type */
public Component[] /* Condition */ getPreConditions();
/**
* @return return the post condition bundled with this ST Type */
public Component[] /* Condition */ getPostConditions();
}
Here an example how the URL ST could look like (just a dummy):
component {
fields=array(
field("URL","url","",true,"URL that will be invoked by the task","text"),
field("Port","port","80",true,"Port of the URL to call (HTTP Default: 80)","text"),
field("Timeout (in seconds)",...),
field("Username",...),
field("Password",...),
...
);
pre=[];
post=[new ConditionJson(this)];
/**
* return the label of the Scheduled Task
*/
public string getLabel() {
return "URL Scheduler";
}
/**
* return the description of the Scheduled Task
*/
public string getDescription(){
return "Scheduled Task that call a URL ...";
}
/**
* return the fields for this ST (see also Cache Driver and Gateway Driver), Railo creates a Form with help of this field, the data populated by the client are then provided to the init method
*/
public Field[] function fields() {
return fields;
}
/**
* called with the settings made in the form based on the given fields
*/
public void function onBeforeUpdate(struct data){
if(not isValid("url",data.url))
throw "url must contain a valid URL";
if(not isNumeric(data.port))
throw "port must be a muneric value";
if(data.port LT 1)
throw "port must posive number";
...
}
/**
* executed the task
* @param data data poluated by the client with help of the Fields.
* @return return true when the task was successfull executed
*/
public boolean function execute(struct data) {
http url="#data.url#" port="#data.port#" result="variables.result" ...;
...
return result.statuscode EQ 200;
}
/**
* @return return the result of the cfhttp
*/
public Struct function getResult() {
return result;
}
/**
*
* @return return the pre condition bundled with this ST Type
*/
public Component[] /* Condition */ getPreConditions() {
return pre;
}
/**
*
* @return return the post condition bundled with this ST Type
*/
public Component[] /* Condition */ getPostConditions() {
return post;
}
}
The introduction of these kind of ST’s allows all different kind of combinations and scenarios.
Conditions
Like you can see in the Example above the task type can return conditions specific to this task type and of course there are the global conditions that can be defined in the group detail.
All these conditions are implemented as simple CFCs, that allow you to define your own conditions. This is the interface a condition has to follow:
interface {
/**
* return the label of the Condition
*/
public string getLabel();
/**
* return the description of the Condition
*/
public string getDescription();
/**
* return the field for this Condition (see also Cache Driver and Gateway Driver), Railo creates a Form with help of this field, the data populated by the client are then provided to the accept method
*/
public Field function field();
/**
* called with the settings made in the form based on the given field
*/
public void function onBeforeUpdate(string value);// throws Exception;
/**
* return true if the condition is accepted
* @param setting value set in the form
*/
public boolean accept(String setting);
}
And again a simple example how an implementation could look like (weekday):
component implements="Condition" {
days="Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday";
field=field("Weekday","weekday",days,true,"Select a Weekday","select");
/** return the label of the Condition */
public string getLabel() {
return "Weekday";
}
/** return the description of the Condition */
public string getDescription() {
return "Only accept a certain Weekday.";
}
/** return the field for this Condition (see also Cache Driver and Gateway Driver), Railo creates a Form with help of this field, the data populated by the client are then provided to the accept method */
public Field function field() {
return field;
}
/** called with the settings made in the form based on the given field */
public void function onBeforeUpdate(string value) {
if(not ListFindNoCase(days,value)
throw "invalid weekday defintion [" & value & "], valid values are [" & days & "]";// this is not really needed because the value come from a select, but this should show how to use.
}
/** return true if the condition is accepted
* @param setting value set in the form
*/
public boolean accept(String weekday) {
return dayOfWeekAsString(dayOfWeek(now())) EQ weekday;
}
}
And an example (Json) for the URL Scheduler:
/** * Used by ScheduleURL Task */ component implements="Condition" { field=field("Version","version","1.0",true,"Json Version","select"); /** * the ST instance */ public boolean init(Component /* ScheduleURL */ st) { variables.st=st; } /** * return the label of the Condition */ public string getLabel() { "Json" } /** * return the description of the Condition */ public string getDescription() { return "check if the returned file content is a json string"; } /** * no filed defintion */ public Field function field() { return field; } /** * called with the settings made in the form based on the given field */ public void function onBeforeUpdate(string value) { // no check needed } /** * return true if the condition is accepted * @param setting value (always "1.0") */ public boolean accept(String setting) { return isJson(st.getResult().fleContent); } }