Library Examples
The following topic contains a number of examples where a library was used to extend Redwood Expression Language.
Expression Constraint to Retrieve all Values of a Specific Column of a Table
By default, the Table constraint retrieves the values from the Key column of a table and likewise, the built-in Table REL functions allow you to lookup using the Key column; the following library source can be used to retrieve the values from a specific column of a table, additionally, a method allows you to filter the values using parameters.
Problem Description
At Example Inc., the SAP systems are installed on different platforms and databases; this is due to acquisitions and mergers of system landscapes. When automating processes on these systems, the IT department uses different chain definitions for each type (OS platform, database, application server type combination) and wants to consolidate those functionally to run on instances of any type. For this, a requirement has been made to be able to retrieve the platform and database flavor for a given instance from within the submit wizard. Also, preconditions are used to skip any processes that are not to run for the type. The IT department has created a table containing the different SAP system names along with OS platform, database flavor, application server type (AS ABAP or AS JAVA). Any column of the table must be accessible via REL, and ideally, drop-downs should be made available in the submit wizard.
The IT department chose a library-based solution, with REL Entry Points, and a table. For drop-downs, simple constraints are used on the chain parameters. The preconditions call the REL Entry Points and evaluate the returned values, skipping any processes that are not to run on a given platform. Download the solution.
Library Source
The Custom_Utils library contains the following code:
package example;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.redwood.scheduler.api.exception.SchedulerAPIPersistenceException;
import com.redwood.scheduler.api.model.APIResultSetCallback;
import com.redwood.scheduler.api.model.ObjectGetter;
import com.redwood.scheduler.api.model.SchedulerSession;
import com.redwood.scheduler.api.scripting.variables.ScriptSessionFactory;
/**
* Create a comma separated String out of the values in a given Column of a
* given Table.
*/
public class ColumnValueJoiner
{
private static final String SQL_SELECT_ALL_COLUMN_VALUES =
"SELECT tv.ColumnValue " +
"FROM TableValue tv " +
"WHERE tv.Table = " +
" ( " +
" SELECT t.UniqueId " +
" FROM Table t " +
" WHERE t.Name = ? " +
" ) " +
"AND tv.ColumnName = ? ";
// Select the "row ids" that match on the filter
private static final String SQL_SELECT_FILTERED_ROW_KEYS =
"SELECT tv.Key " +
"FROM TableValue tv " +
"WHERE tv.Table = " +
" ( " +
" SELECT t.UniqueId " +
" FROM Table t " +
" WHERE t.Name = ? " +
" ) " +
"AND tv.ColumnName = ? " +
"AND tv.ColumnValue = ? ";
private static final String SQL_SELECT_FILTERED_COLUMN_VALUES =
SQL_SELECT_ALL_COLUMN_VALUES + " AND tv.Key IN (" + SQL_SELECT_FILTERED_ROW_KEYS + ")";
/**
* @param aTableName
* @param aColumnName
* @return a comma separated String of the column values associated with
* a TableName.aColumnName.
* @throws SchedulerAPIPersistenceException
*/
public static String join(String aTableName, String aColumnName)
throws SchedulerAPIPersistenceException
{
return join(SQL_SELECT_ALL_COLUMN_VALUES, new Object[] { aTableName, aColumnName });
}
/**
* @param aTableName
* @param aColumnName
* @param aFilterColumn
* @param aFilterValue
* @return a comma separated String of the column values associated with aTableName.aColumnName,
* but limited to the rows where aTableName.aFilterColumn has the value aFilterValue.
* @throws SchedulerAPIPersistenceException
*/
public static String filterAndJoin(String aTableName, String aColumnName, String aFilterColumn, String aFilterValue)
throws SchedulerAPIPersistenceException
{
if(aFilterValue == null || aFilterValue.trim().length() == 0)
{
return "Error";
}
return join(SQL_SELECT_FILTERED_COLUMN_VALUES, new Object[]{aTableName, aColumnName, aTableName, aFilterColumn, aFilterValue});
}
private static String join(String aSql, Object[] aParams) throws SchedulerAPIPersistenceException
{
SchedulerSession session = ScriptSessionFactory.getSession();
ColumnValueJoinerCallback callback = new ColumnValueJoinerCallback();
session.executeQuery(aSql, aParams, callback);
return callback.getResult();
}
/**
* Handler of the ResultSet.
*/
private static class ColumnValueJoinerCallback
implements APIResultSetCallback
{
private final StringBuilder result;
ColumnValueJoinerCallback()
{
result = new StringBuilder();
}
/** {@inheritDoc} */
@Override
public void start()
{
// Nothing to do
}
/** {@inheritDoc} */
@Override
public boolean callback(ResultSet aRs, ObjectGetter aObjectGetter)
throws SQLException
{
if (result.length() > 0)
{
result.append(",");
}
result.append(aRs.getString(1));
return true;
}
/** {@inheritDoc} */
@Override
public void finish()
{
// Nothing to do
}
public String getResult()
{
return result.toString();
}
}
}
REL Entry Points
The following REL entry points were created:
Name | FQ Class Name | Method Signature |
---|---|---|
joinColumnValues | example.ColumnValueJoiner | join(java.lang.String, java.lang.String) |
filterAndJoinColumnValues | example.ColumnValueJoiner | filterAndJoin(java.lang.String, java.lang.String, java.lang.String, java.lang.String) |
Table
The following table contains an extract from the SAPSystemLandScape table used in this example. Note that PR1
is a dual-stack SAP system.
Key | SAP_System | AS | Platform | Database |
---|---|---|---|---|
1 | PR1 | JAVA | UNIX | Oracle |
2 | PR1 | ABAP | UNIX | Oracle |
3 | ER1 | JAVA | Windows | MSSQL |
4 | ER2 | ABAP | Windows | DB2 |
Process Parameters
The following process definition parameters have been defined:
Parameter Name | Default Value | Simple Constraint Type | Simple Constraint Data |
---|---|---|---|
SAP_SID | Expression | =Constraint.listConstraint('SID', Custom_Utils.joinColumnValues('SAPSystemLandScape', 'SAP_SYSTEM'), true) | |
SAP_Database | Expression | =Constraint.listConstraint('Database', Custom_Utils.filterAndJoinColumnValues('SAPSystemLandScape', 'Database', 'SAP_SYSTEM', parameters.SAP_SID), true) | |
AS | Expression | =Constraint.listConstraint('Database', Custom_Utils.filterAndJoinColumnValues('SAPSystemLandScape', 'Application Server', 'SAP_SYSTEM', parameters.SAP_SID), true) |
Retrieve Email Address for a User
Problem Description
The development department at Example Inc. wants to be able to retrieve the email address of a given user. Redwood Server synchronizes user metadata with the authentication provider each time the user logs in, the development department decided to retrieve the email address from Redwood Server using a library and with REL Entry Points.
In this example, one class is used for both RedwoodScript and Redwood Expression Language. This is for illustration purposes only to show you how to access your methods from both. Redwood recommends to keep methods for RedwoodScript and Redwood Expression Language in different libraries as libraries get recompiled.
The chosen solution uses the Custom_UserUtils library to store the RedwoodScript code to retrieve the email address of a user and REL Entry Points to access the library from within RedwoodExpressionLanguage expressions.
Library Code
package example;
//Example code to illustrate
import com.redwood.scheduler.api.model.SchedulerSession;
import com.redwood.scheduler.api.model.Subject;
import com.redwood.scheduler.api.model.enumeration.SubjectType;
import com.redwood.scheduler.api.scripting.variables.*;
public class user
{
/**
* Checks to see if the email address is valid and ends with the corporate domain
* Uses the default email address if anything is wrong
*
* @param username the username to use for looking up the email address
* @return the email address for the username or, if there is a problem, the default email address
*/
public static String getEmailAddress(String username)
{
//Default email address is operators@example.com
String defaultEmail = "operators@example.com";
if (username == null || username.trim().isEmpty())
{
return defaultEmail;
}
SchedulerSession jcsSession = ScriptSessionFactory.getSession();
Subject subject = jcsSession.getSubjectByTypeName(SubjectType.User, username);
String email = "";
if (subject != null)
{
email = subject.getEmail();
}
{
if (email != null && email.contains("@"))
//assume it is an email address a return it
return email;
}
return defaultEmail;
}
}
REL Entry Points
- Name -
getEmail
- FQ Class Name -
example.user
- Method Signature -
getEmailAddress(java.lang.String)
Use in Redwood Expression Language on a Lone Job
In a parameter:
=Custom.getEmail('Administrator')
The following example was used in a process definition with two parameters, the first named Username
and the second, which has the Runtime
option (down the bottom of the parameter dialog) checked, named Email
. The display order attribute defines the order at which parameters are displayed in the submit wizard as well as the evaluation sequence. The Runtime
option was used so that the parameter is evaluated at runtime.
=Custom.getEmail(parameters.Username)
Use in a Chain
A chain definition has a parameter Username
. The chain process in the third step calls System_Mail_Send. The To
parameter of the System_Mail_Send chain process was set to =Custom.getEmail(chainParameters.Username)
and would use the parameter on the chain to retrieve the correct email address.
Use in RedwoodScript
In a Process Definition
- Create a RedwoodScript process definition, choose RedwoodScript as definition type and the library you added the above code to as library.
- Give the process definition a name on the Definition tab.
- On the Parameters tab, create a parameter named
Username
. - On the Definition enter the following code into the Source field:
{
jcsOut.println(example.user.getEmailAddress(Username));
}
An alternative is to import the class, so you do not have to specify the package name every time:
import example.user;
{
jcsOut.println(user.getEmailAddress(Username));
}
String Functions
Problem Description
You need additional string functions for use in process parameters.
Library Source
The Custom_StringUtils library, that can be downloaded, contains the following code:
package example;
//Example code to illustrate
public class StringUtils
{
/**
* Checks to see if the String matches regex pattern
* @param input string
* @param pattern regex pattern
* @return boolean
*/
public static Boolean matches(String input, String pattern)
{
if (input == null)
{
return false;
}
return input.matches(pattern);
}
/**
* Replace pattern in input String
* @param input string
* @param pattern pattern to match
* @param replace string to replace pattern with
* @return String
*/
public static String replace(String input, String pattern, String replace)
{
if (input == null)
{
return "";
}
return input.replace(pattern, replace);
}
/**
* Replace pattern in input String
* @param input string to replace
* @param pattern regex pattern to match
* @param replace string to replace pattern with
*
* @return String
*/
public static String replaceAll(String input, String pattern, String replace)
{
if (input == null)
{
return "";
}
return input.replaceAll(pattern, replace);
}
}
REL Entry Points
Name | FQ ClassName | Method Signature |
---|---|---|
matches | example.StringUtils | matches(java.lang.String, java.lang.String) |
replace | example.StringUtils | replace(java.lang.String, java.lang.String, java.lang.String) |
replaceAll | example.StringUtils | replaceAll(java.lang.String, java.lang.String, java.lang.String) |
See Also
libraries