Using the RedwoodScript Definition Type
RedwoodScript is available with the Scripting module; please check the License section for more information.
RedwoodScript is based on the Java language. It allows you to create Process Definitions with RedwoodScript in its source. The RedwoodScript Definition Type is used to create processes that interact primarily with the server interfaces themselves. For instance, you can write a process that submits other processes or retrieves and combines meta data or output from other processes.
Note: You must assign at least one Process Server to run RedwoodScript Process Definitions in order to use the Definition Type.
Variable and Parameters
Variables that you require to hold intermediary values are defined in the same way as you would normally define variables in Java, using the standard syntax String myVar
or String myVar = "initial value"
. Array parameters are available for RedwoodScript and are accessed like regular Java arrays. Arrays must have unique elements.
Process Definition parameters are available directly in RedwoodScript as Java variables, as shown in the examples below.
Note: When a process reaches status Error, the Out values of parameters are not set. You can use a Post Running action to set them if required.
Completion Strategy
A completion strategy defines how a process is to have reached a final state. By default, RedwoodScript Process Definitions behave like other Definition Types, when processing of the source has completed or is interrupted (by an error or Redwood Server shutdown), the Process Definition reaches a final state. The following completion strategies are available:
Default
- sets the Process Definition to a final state once its thread has completed.External
- the status of the Process Definition will be determined externally; a caller will set it for Redwood Server.ExternalWaitForChildJobs
- the status of the Process Definition will be determined externally; a caller will set it for Redwood Server, however, the process will wait for its children.Resilient
- the process survives central Redwood Server shutdowns. Once the central Redwood Server is restarted, the process is restarted; a process must be restartable to be able to use this completion strategy reliably.
RedwoodScript Versions
By default, RedwoodScript is based on Java. You can set the /configuration/javatoolkit/SourceVersion
and /configuration/javatoolkit/TargetVersion
registry entries to your desired version and can use all the features of the version you specify.
Examples
The following code shows how to access the process's input parameter names and values:
{
jcsOutLog.debug("Parameters for process " + jcsJob.getJobId());
for (JobParameter parameter : jcsJob.getJobParameters())
{
jcsOutLog.debug(parameter.getJobDefinitionParameter().getName() + "=" + parameter.getInValue());
}
}
Within a Chain you can not only access the parameters of the current process, but also the parameters of the Chain, if needed. The following code shows how to do this, by instantiating an object that is the grandparent of the current Chain Process. The parent of the Chain Process is the Step within the Chain, and the grandparent is the Chain Process for the Chain itself. If you need to access further ancestors the method would be to call getParentJob()
recursively. The following code can also be used to execute code depending on the depth of the Chain Process in the Chain, or to detect if the process is part of a Chain or not.
{
// Does this process have a step - which would imply that we are part of a chain
if (jcsJob.getJobChainStep() != null)
{
JobChain chain = jcsJob.getJobChainStep().getJobChain();
if (chain != null)
{
Job jcjob = jcsJob.getParentJob().getParentJob();
// We are in a chain, our parent's parent is the chain job
// Since our parent is a step, the step's parent is the chain.
jcsOutLog.debug("Part of a chain " + chain.getJobDefinition().getName());
jcsOutLog.debug("Chain parameters");
for (JobParameter parameter : jcjob.getJobParameters())
{
jcsOutLog.debug(parameter.getJobDefinitionParameter().getName() + "=" + parameter.getInValue());
}
}
}
else
{
jcsOutLog.debug("Not in a chain");
}
}
Resilient Completion Strategy
The following code submits three processes in 30 second intervals, the process is persistent, which means that it survives a restart of the central Redwood Server. The code stores the state in the ResilientTable table, which means that it keeps track of the last phase it was in and only submits the remaining processes, if any.
This code is for illustration purposes, only. You will need to create a table definition and table, named ResilientTable
, with a Value column for this example to work in your environment.
package customer;
import java.math.BigDecimal;
import com.redwood.scheduler.api.exception.*;
import com.redwood.scheduler.api.model.*;
import com.redwood.scheduler.api.model.enumeration.*;
public class ResilientExample
extends ResilientExampleStub
{
private static final int PHASE1 = 1;
private static final int PHASE2 = 2;
private static final int PHASE3 = 3;
private static final BigDecimal SLEEP_SECONDS = new BigDecimal("30000");
private static final String TABLE_PARTITION = "GLOBAL";
private static final String TABLE_NAME = "ResilientTable";
private static final String COLUMN_NAME = "Value";
public void execute()
throws Exception
{
final CompletionStrategyType strategy = jcsJob.getCompletionStrategy();
if (strategy == null)
{
/*
* When making the job resilient from within the script, you need to call this
* as early as possible. The better solution is to set the completion strategy
* on the definition to be Resilient, that way you do not have to worry about
* the the job being terminated before it can set the completion strategy.
*/
jcsJobContext.becomeResilient();
}
jcsOut.println("Starting job run");
/*
* We need to determine where in the process to run. If this is the first run,
* then we start from the beginning, however if the job has been restarted, the
* job started running before, but didn't finish, most likely because of a
* system restart, and now we get a chance to finish it.
*/
final int startPhase = determineStartPhase();
process(startPhase);
}
private void process(final int startPhase)
throws Exception
{
switch (startPhase)
{
case PHASE1:
processPhase1();
// FALLTHROUGH - in normal processing we run phase 2 when phase 1 finishes
case PHASE2:
processPhase2();
// FALLTHROUGH - in normal processing we run phase 3 when phase 2 finishes
case PHASE3:
processPhase3();
}
}
private void processPhase1()
throws Exception
{
jcsOut.println("Phase 1");
saveProgress(PHASE1);
/*
* This will assure that child processes of this job will get killed if this job
* is killed. In our example that would be the child System_Sleep
*/
jcsJobContext.killJobWithParent(jcsJob);
// As an example submit a child job sleep (30sec sleep)
final JobDefinition jd = jcsSession.getJobDefinitionByName("System_Sleep");
final Job job = jd.prepare();
job.getJobParameterByName("MilliSeconds").setInValueNumber(SLEEP_SECONDS);
// The persist after progress, so the above succeeds together with storing
// the progress (so in one transaction)
jcsSession.persist();
// Wait for all children of jcsJob
jcsJobContext.waitForAllChildren(jcsSession);
}
private void processPhase2()
throws Exception
{
jcsOut.println("Phase 2");
saveProgress(PHASE2);
// Do things, in this example, we simply start a child sleep job
final JobDefinition jd = jcsSession.getJobDefinitionByName("System_Sleep");
final Job job = jd.prepare();
job.getJobParameterByName("MilliSeconds").setInValueNumber(SLEEP_SECONDS);
jcsSession.persist();
jcsJobContext.waitForAllChildren(jcsSession);
}
private void processPhase3()
throws Exception
{
jcsOut.println("Phase 3");
saveProgress(PHASE3);
// Do things, in this example, we simply start a child sleep job
final JobDefinition jd = jcsSession.getJobDefinitionByName("System_Sleep");
final Job job = jd.prepare();
job.getJobParameterByName("MilliSeconds").setInValueNumber(SLEEP_SECONDS);
jcsSession.persist();
jcsJobContext.waitForAllChildren(jcsSession);
}
private TableValue getTableValue()
throws Exception
{
Partition p = jcsSession.getPartitionByName(TABLE_PARTITION);
Table t = jcsSession.getTableByName(p, TABLE_NAME);
TableValue tv = t.getTableValueBySearchKeySearchColumnName(jcsJob.getJobId().toString(), COLUMN_NAME);
if (tv != null)
{
return tv;
}
else
{
tv = t.createTableValue();
tv.setColumnName(COLUMN_NAME);
tv.setColumnValue("1");
tv.setKey(jcsJob.getJobId().toString());
jcsSession.persist();
return tv;
}
}
private void saveProgress(int phase)
throws SchedulerAPIPersistenceException
{
/*
* Store phase (possibly more data), in a jobfile, or in a parameter, or in a
* table, or something that survives, so on a new run of this process can figure
* out how far this process has completed, then start on the next phase.
*
* For this example, we are storing the phase in table.
*/
TableValue tv = getTableValue();
tv.setColumnValue(Integer.toString(phase));
jcsSession.persist();
}
private int determineStartPhase()
{
// Lookup the stored progress, in this example, this is a phase number.
TableValue tv = getTableValue();
int phase = Integer.parseInt(tv.getColumnValue());
if (phase > 0)
{
return phase;
}
else
{
return 0;
}
}
}
More examples are available in the Using RedwoodScript in Processes section.