Using RedwoodScript in Job Definitions

RedwoodScript is the ideal language to automate complex tasks, adding custom code to a Job Definition, for example. The following code illustrates some features you might like to implement.

Note: The easiest way to develop your code is to use the web-based shell first. If it runs in there, it should run in a Job Definition. However, the web-based shell does not have parent processes, so parent/child examples will not run in it.

See the RedwoodScript, Redwood Expression Language, and Scripting Contexts sections for a list of examples, functions, contexts and predefined objects.

Note: Redwood Software does not warrant that this is the most effective way in solving your specific problem. The examples are provided to illustrate the use of RedwoodScript.

Note: If you set the Requested Start Time of a Job, pay attention to not specify dates before 1901 or your Job will be vetoed.

Creating a Job Definition

To create a Job Definition, you first need to know if the Definition Type requires a source. Then you create the Job Definition and source (in a Script object) and link the source to the process.

Copy
{
  JobDefinition jDefinition = jcsSession.createJobDefinition();
  jDefinition.setName("JD_GetCurrentSales");
  jDefinition.setPartition(jcsSession.getPartitionByName("SALES"));
  jDefinition.setJobDefinitionType(jcsSession.getJobDefinitionTypeByName("JDBC"));
  Script script = jcsSession.createScript();
  script.setLibrary(jcsSession.getLibraryByName("Custom"));
  script.setJobDefinition(jDefinition);
  script.setRunAsUser("Example");
  script.setSource("select prod_ID,prod_name,prod_inc,prod_out from prod where prod_mon = 123");
  jcsSession.persist();
}

Accessing Parameters

To access the current Parameters, remember that the current Job is already defined as jcsJob and the SchedulerSession is defined as jcsSession.

Copy
{
  jcsOut.println(Name);
  jcsOut.println(Address);
  jcsOut.println(City);
  jcsOut.println(Zip);
}

You can also use the Job (technical name Job ) to retrieve the Parameters and then their values. The below code can also be used if you want to retrieve the values of Parameters of other Jobs (replace the otherJob object with an object that points to the correct Job).

Copy
{
  // Replace this line with code to get the other process
  Job otherProcess = jcsJob;

  //get the In value of parameter pName and print its value
  JobParameter pNameVal = otherProcess.getJobParameterByName("pName");
  jcsOut.println(pNameVal);

  //or, without retrieving the parameter
  jcsOut.println(pName);

  //set the Out value of the parameter
  pName = "GoodBye World!";

  //print out the Out value
  jcsOut.println(pName);

  //write it to the database
  jcsSession.persist();
}

Running a Simple Job Definition

Basic code to create a Job from a Job Definition, without specifying any Parameters. If the Job Definition has Parameters, the defaults will be used.

Copy
{
  // code to submit a job running System_Sleep
  // get the job definition
  JobDefinition jDefinition  = jcsSession.getJobDefinitionByName("System_Sleep");

  // create the job from the job definition
  Job process = jDefinition.prepare();

  // submit the job definition and write unsaved data to the database
  jcsSession.persist();
}

Submitting a Simple Job with Parameters

The following is the same basic code as above, but this time with Parameters. If you do not specify a value for a Parameter, the default value will be used. If a Job Definition has a required parameter with no default value, you must specify a value or the Job will not run.

Copy
{
  // code to submit SAP_AbapRun that runs the ABAP program RSUSR000
  // get the job definition SAP_AbapRun
  JobDefinition jDefinition  = jcsSession.getJobDefinitionByName("SAP_AbapRun");

  // create the job
  Job process = jDefinition.prepare();

  // assign parameters
  process.getJobParameterByName("SAP_SYSTEMS").setInValueString("TR1");
  process.getJobParameterByName("ABAP_PROGRAM_NAME").setInValueString("RSUSR000");
  process.getJobParameterByName("JOBNAME").setInValueString("RSUSR000 made with RedwoodScript");

  // assign queue as SAP jobs require a queue
  Partition Partition = jcsSession.getPartitionByName("RW_DEMO");
  Queue queue = jcsSession.getQueueByName(partition, "TR1_Queue");
  process.setQueue(queue);

  // submit the job definition and write unsaved data to the database
  jcsSession.persist();

  // wait for all children
  jcsSession.waitForAllChildren(process);
}

Running a Simple Job Definition with Parameters Using REL Evaluation

The following is the same basic code as above, but this time specifying a REL expression as a value. The expression will be evaluated and the Parameter value will be set to the result of the expression.

Copy
{
  // code to submit SAP_AbapRun that runs the ABAP program RSUSR003
  // get the job definition SAP_AbapRun
  JobDefinition jDefinition  = jcsSession.getJobDefinitionByName("SAP_AbapRun");

  // create the job
  Job process = jDefinition.prepare();

  // assign parameters
  process.getJobParameterByName("SAP_SYSTEMS").setInValueString("TR1");
  process.getJobParameterByName("ABAP_PROGRAM_NAME").setInValueString("RSUSR003");
  process.getJobParameterByName("JOBNAME").setInValueByEvaluatingREL(jcsSession, "='From ' + getSystemId()");

  // assign queue as SAP jobs require a queue
  Partition Partition = jcsSession.getPartitionByName("RW_DEMO");
  Queue queue = jcsSession.getQueueByName(partition, "TR1_Queue");
  process.setQueue(queue);

  // submit the job definition and write unsaved data to the database
  jcsSession.persist();

  // wait for all children
  jcsSession.waitForAllChildren(process);
}

Accessing Parent/Child Parameters

In this example, a child Job retrieves the Parameter values of its direct parent. Remember that a Job Call has a Step as a parent, and a Step has no Parameters.

Copy
{
  //get the parent job
  Job parent =  jcsJob.getParentJob();

  //get the job parameters and respective values
  JobParameter parentName = parent.getJobParameterByName("pName");
  JobParameter parentName2 = parent.getJobParameterByName("pName2");
  Object parentNameInValue = parentName.getInValue();
  Object parentName2InValue = parentName2.getInValue();

  //log the values of the parent parameters
  jcsOutLog.info("Parent Parameter pName is set to " + parentNameInValue);
  jcsOutLog.info("Parent Parameter pName2 is set to " + parentName2InValue);

  //set the Out value of the current (child) job
  jcsJob.getJobParameterByName("childParam").setOutValue("Done");
  jcsSession.persist();
}

Accessing Workflow Parameters

In this example, a Job Call retrieves Workflow-level Parameters.

Copy
{
  //get the step job (up one level)
  Job step = jcsJob.getParentJob();

  //get the inner-most chain process (up another level)
  Job parent = step.getParentJob();

  //get the parameters and respective values
  JobParameter chainName = parent.getJobParameterByName("pName");
  JobParameter chainName2 = parent.getJobParameterByName("pName2");
  Object chainNameInValue = parentName.getInValue();
  Object chainName2InValue = parentName2.getInValue();

  //log the values of the parameters
  jcsOutLog.info("Parent Parameter pName is set to " + chainNameInValue);
  jcsOutLog.info("Parent Parameter pName2 is set to " + chainName2InValue);

  //set the Out value of the current job
  jcsJob.getJobParameterByName("JobpName").setOutValue("Done");
  jcsSession.persist();
}

Another example:

Copy
{
  //get the parameters and respective values
  JobParameter chainName = jcsJob.getParentJob().getParentJob().getJobParameterByName("pName");
  JobParameter chainName2 = jcsJob.getParentJob().getParentJob().getJobParameterByName("pName2");

  //log the values of the parent parameters
  jcsOutLog.info("Parent Parameter pName is set to " + chainName);
  jcsOutLog.info("Parent Parameter pName2 is set to " + chainName2);

  //set the Out value of the current (child) job
  childParam = "Done";
  jcsSession.persist();
}

Deleting Jobs that Ran on a Job Server Using executeObjectQuery

Assume you want to delete all the Jobs on a Job Server because you want to delete the Job Server. The following example illustrates how to delete all the processes that ran on Job Server MSLN_WINS3.

Copy
{
  // Note that the following query uses query parameters for the
  // query. User content should never be injected directly into
  // the SQL query that is sent to the database.
  String query = "select Job.* from Job where ProcessServer = ?";
  for (Job process : jcsSession.executeObjectQuery(Job.TYPE, query, new Object[] {jcsSession.getProcessServerByName("MSLN_WINS1").getUniqueId()}))
  {
    if(process.getStatus().getState() == JobStatusState.Final)
    {
      jcsOut.println("Deleting process "
                     + process.getDescription()
                     + ", which has the status "
                     + process.getStatus()
                     + ".");
      process.deleteObject();
    }
    jcsSession.persist();
  }
}

Counting the Number of Jobs on a Job Server using executeQuery

Copy
{
  LongCallBack callback = new LongCallBack(1);
  String query = "select count(*) from Job where Job.ProcessServer = ?";
  try
  {
    jcsSession.executeQuery(query, new Long[] { jcsSession.getProcessServerByName("MSLN_WINS3").getUniqueId() }, callback);
  }
  catch (Exception e)
  {
    throw new RuntimeException(e);
  }
  Long myCount = (Long) callback.getResult().get(0);

  if (myCount != null)
  {
    jcsOut.println(myCount);
  }
}

Creating Workflow Definitions in RedwoodScript

Creating Workflow Definitions in RedwoodScript is a multi-step process. You must first create a JobDefinition, then create a JobChain and set the Workflow Definition to your newly created Job Definition. You can then create child Steps and Job Call children of the Steps.

Copy
{
  String pName = "RW_DEMO";
  String jDefinitionName = "JC_Sleep";
  String stepName = "Step 1";

  //get or create  a Partition (here we create one if it does not exist)
  Partition Partition = jcsSession.getPartitionByName(pName);
  if (partition == null)
  {
    Partition = jcsSession.createPartition();
    Partition.setName(pName);
  }

  JobDefinition jDefinition = jcsSession.getJobDefinitionByName(partition, jDefinitionName);
  if (jDefinition == null)
  {
    //Create JobDefinition of type JOB_CHAIN
    jDefinition = jcsSession.createJobDefinition();
    jDefinition.setName(jDefinitionName);
    jDefinition.setJobDefinitionType(jcsSession.getJobDefinitionTypeByName(JobDefinitionType.JOB_CHAIN));
    jDefinition.setPartition(partition);
    //Create jobchain object with one step and one job chain call
    JobChain jWorkflow = jcsSession.createJobChain();
    jWorkflow.setJobDefinition(jDefinition);
    //step
    JobChainStep jcStep = jWorkflow.createJobChainStep();
    jcStep.setSequenceNumber(Long.valueOf(0));
    jcStep.setName(stepName);
    //job chain call (chain process)
    JobChainCall jcCall = jcStep.createJobChainCall();
    JobDefinition jDefinitionSleep = jcsSession.getJobDefinitionByName("System_Sleep");
    jcCall.setJobDefinition(jDefinitionSleep);
    jcCall.setSequenceNumber(Long.valueOf(0));
  }
  else
  {
    jcsOut.println("JobDefinition " + jDefinitionName + " exists!");
    jcsOut.println("You can change the name of the JobDefinition on the line:\n String jDefinitionName = \"JC_Sleep\";");
  }
  jcsSession.persist();
}

Creating User Messages in RedwoodScript

Copy
{
  String pName = "RW_DEMO";
  String uMessageName = "UM_ReviewWorldSalesReport";

  //get or create  a Partition (here we create one if it does not exist)
  Partition Partition = jcsSession.getPartitionByName(pName);
  if (partition == null)
  {
    Partition = jcsSession.createPartition();
    Partition.setName(pName);
  }

  JobDefinition jDefinition = jcsSession.getJobDefinitionByName(partition, uMessageName);
  if (jDefinition == null)
  {
    JobDefinition jd = jcsSession.createJobDefinition();
    jd.setName(uMessageName);
    jd.setPartition(partition);
    JobDefinitionType um = jcsSession.getJobDefinitionTypeByName("UserMessage");
    jd.setJobDefinitionType(um);
    UserMessageDefinition umd = jcsSession.createUserMessageDefinition();
    umd.setJobDefinition(jd);
    umd.setText("Please review world sales report.");
    UserMessageDefinitionResponse umr = umd.createUserMessageDefinitionResponse();
    umr.setStatus(JobStatus.Completed);
    umr.setName("Done");
    umr.setReturnCode(Long.valueOf(0));
    umr = umd.createUserMessageDefinitionResponse();
    umr.setStatus(JobStatus.Error);
    umr.setName("Failed");
    umr.setReturnCode(Long.valueOf(1));
    UserMessageDefinitionParticipant umdp = umd.createUserMessageDefinitionParticipant();
    umdp.setType(SubjectType.Role);
    umdp.setName("scheduler-user");
    //The following only works in a chain with a process at position 1,
    //in the step named "Step 1" producing output in a file named stdout.log - for illustration
    //purposes, only.
    //UserMessageDefinitionAttachment umda = umd.createUserMessageDefinitionAttachment();
    //umda.setDescription("World Sales Report");
    //umda.setSpecification("Step 1, Job 1:stdout.log");
  }
  else
  {
    jcsOut.println("JobDefinition " + uMessageName + " exists!");
    jcsOut.println("You can change the name of the JobDefinition on the line:\n String uMessageName = \"UM_ReviewWorldSalesReport\";");
  }
  jcsSession.persist();
}

Creating a Job File in RedwoodScript

Copy
{
    JobFile jf = jcsJob.createJobFile();
    jf.setFileType(JobFileType.Output);

    jf.setName("SomeTextFile.txt");
    jf.setFormat(jcsSession.getFormatByName("HTMLText")); //You can also create your own in Definitions > Formats

    jf.setOrder(JobFile.CUSTOMER_ORDER_START); //Sort order must be >= CUSTOMER_ORDER_START
    jcsOut.println(jf.getType().getCodeExString());
    jf.setFileNameAutomatic(); //Filename must be generated
    jcsSession.persist();

    String fileToWriteTo = jf.getFileName();

    //Standard Java code to create and populate the actual file
    java.io.PrintWriter writer = new java.io.PrintWriter(fileToWriteTo, "UTF-8");
    writer.println("The first line");
    writer.println("The second line");
    writer.close();
}