Scripting

Scripting basics

The languages, the Java bridge, the variable systems, and where each fits.

JavaScript (Nashorn)

Etlworks's default and recommended scripting language. The engine is Nashorn: the JavaScript implementation embedded in the JVM. Nashorn supports:

Returning a value

Two equivalent forms: pick whichever reads better:

JavaScript
// Assign to the special `value` variable
value = firstName + " " + lastName;

// Or rely on the last evaluated expression
firstName + " " + lastName;

Loading external JavaScript libraries

JavaScript
// From a URL
load("https://example.com/scripts/utility.js");

// From the local file system
load("file:///opt/scripts/functions.js");
Nashorn references

For a deep dive on the engine itself: Oracle Nashorn overview.

Python (3.13)

Embedded Python is powered by GraalPy on the Java 25 runtime and is Python 3.13. It is available wherever JavaScript is available: the same scripting fields, the same runtime objects.

Upgrading from 9.x? Review your Python scripts

Etlworks 10.x replaced Jython (Python 2.7) with GraalPy. There is no Jython compatibility switch, and scripts are not translated automatically. Scripts written in syntax common to Python 2 and 3 may run unchanged, but every script should be reviewed. 9.x runtimes and 9.x Integration Agents still run Jython, and the engine that executes a flow decides which Python it gets, so test on the actual execution target when application and Agent versions differ.

Migration checklist in the Etlworks KB →

Embedded PythonExternal Python (Execute Script flow)
Runs in the Etlworks JVMA separate interpreter process
Python 3.13 with the bundled standard library: json, re, datetime, decimal, pathlib and the restYour own interpreter and virtual environment
No pip. Compatible pure-Python modules can be imported from a directory you add to sys.pathFull pip, virtual environments, any package the interpreter supports
Can load any Java class available to the runtimeNo access to the JVM
For inline logic close to your data and flowsFor package-heavy programs and standalone processes

Returning a value

Both forms work. A final expression supplies the result, the same way JavaScript does, and the scriptResult callback is still supported:

Python
value = dataSet.getFieldValue(currentRow, "amount") * 1.2
value
Python
value = dataSet.getFieldValue(currentRow, "amount") * 1.2
scriptResult.setResult(value)

A non-null evaluated expression takes precedence; when the script evaluates to nothing, Etlworks falls back to the scriptResult value. Do not set both to different values. Note that a trailing assignment is not a trailing expression: end with the bare expression when you want it returned. A top-level return is not valid outside a function.

Loading Java classes from Python

Use java.type(...). The Jython-style from com.example import SomeClass no longer works:

Python
import java

Jsoup = java.type("org.jsoup.Jsoup")
FileManagerTask = java.type("com.toolsverse.etl.core.task.common.FileManagerTask")
TypedKeyValue = java.type("com.toolsverse.util.TypedKeyValue")
ArrayList = java.type("java.util.ArrayList")

The class has to be available to the runtime that executes the script; java.type(...) does not download anything. Wrap returned Java values in str(...) before using Python string operations on them, and build a Java collection when a Java API expects one rather than passing a Python list.

Need pandas, numpy or boto3?

Embedded Python has no pip and bundles no third-party packages. Use the Execute Script Local or Remote via SSH flow, which runs your own interpreter with its own virtual environment and packages. Choose by workload rather than by Python version: embedded Python is already Python 3.13.

Use Python in the Etlworks KB →


Importing Java classes

Four ways to reference a Java class from JavaScript. They all produce the same result: pick by readability.

1. Fully qualified name (recommended for one-off use)

JavaScript
var list = new java.util.ArrayList();

2. importPackage

JavaScript
importPackage(java.util);
var list = new ArrayList();

3. Java.type("…") (recommended when you'll use the class repeatedly)

JavaScript
var ArrayList = Java.type("java.util.ArrayList");
var list = new ArrayList();

4. JavaImporter (flexible, slower: avoid in hot loops)

JavaScript
var javaImports = new JavaImporter(
  java.util,
  com.toolsverse.util
);
with (javaImports) {
  var list = new ArrayList();
  var ds   = new DataSet();
}

Etlworks API reference

The most useful packages and classes you'll reach for from a script. Full Javadoc is published with the platform; this is a curated index.

Common packages

PurposePackage
Utility classescom.toolsverse.util
Loggingcom.toolsverse.util.log
System configurationcom.toolsverse.config
Common ETL engine classes (DataSet, FieldDef, …)com.toolsverse.etl.common
ETL engine configurationcom.toolsverse.etl.core.config
ETL engine corecom.toolsverse.etl.core.engine
Common ETL taskscom.toolsverse.etl.core.task.common
Java collections and utilitiesjava.util

Utility methods: com.toolsverse.util.Utils

JavaScript
if (com.toolsverse.util.Utils.isNothing(value)) {
  // value is null, undefined, empty string, empty collection, etc.
}

var fileName = com.toolsverse.util.FilenameUtils.getName(path);

File / connection IO: com.toolsverse.etl.core.task.common.FileManagerTask

JavaScript
// Check for files matching a pattern on a named connection
var found = com.toolsverse.etl.core.task.common.FileManagerTask.filesExist(
  etlConfig, "connectionName", "*.json"
);

// List files
var list = com.toolsverse.etl.core.task.common.FileManagerTask.list(
  etlConfig, "connectionName", "*.json"
);
if (list != null) {
  for each (var file in list) {
    etlConfig.log("Name:" + file.getName() +
                  " Size:" + file.getSize() +
                  " Path:" + file.getPath() +
                  " Last modified:" + file.getLastModified());
  }
}

// Write
com.toolsverse.etl.core.task.common.FileManagerTask.write(
  etlConfig, "connectionName", filename, payload
);

// Read
var data = com.toolsverse.etl.core.task.common.FileManagerTask.read(
  etlConfig, "connectionName", filename
);

// Execute an HTTP call against a manually-built Alias
var alias = new com.toolsverse.etl.common.Alias();
alias.setUrl("http://localhost:8080/health");
alias.setTransport("com.toolsverse.io.HttpProcessor");
alias.setParams("method=GET");
var response = com.toolsverse.etl.core.task.common.FileManagerTask.execute(
  alias, null, true
);

// Or via a named connection
var response = com.toolsverse.etl.core.task.common.FileManagerTask.execute(
  etlConfig, "connection_name", null, true
);

Data: DataSet, FieldDef, DataSetRecord

JavaScript
// Read a value
var fldValue = dataSet.getFieldValue(currentRow, "InvoiceNo");

// Get the field definition by name
var fldName = dataSet.getFieldDef("InvoiceNo").getNameToUse();

// Get a record by index
var record = dataSet.getRecord(0);

// High-level transformations across two datasets
var newDs = com.toolsverse.etl.common.CommonEtlUtils.intersect(dataSet, with, "id");

Connection & engine config: Alias, EtlConfig

JavaScript
// Connection (Alias) by name from the current flow
var alias = etlConfig.getAliasesMap().get("Connection name");

// Direct connection access
var connection = etlConfig.getConnection("Postgres");

Scenario: com.toolsverse.etl.core.engine.Scenario

JavaScript
var name = scenario.getName();
var v = scenario.getVariable("MY_VAR");
v.setValue("new value");

For the full reference (including SqlUtils, Extractor, ConnectorUtils, anonymizer providers, and many more), see the Recipes and Reference pages, they cover the methods you'll actually use, with examples.

External libraries

Custom Java libraries

Drop your own JAR onto the classpath and call it directly from JavaScript.

DeploymentHow to install
Self-hosted EtlworksCopy the JAR into TOMCAT_HOME/lib, restart Tomcat. Then var X = Java.type("com.mycompany.X");
Etlworks CloudContact support@etlworks.com to upload custom libraries.

External JS libraries

Use Nashorn's built-in load(): loads from URL or file system. See the JavaScript section above.


Global variables

Tenant-scoped key/value pairs accessible across flows. Strings only.

Set

JavaScript
// props is a java.util.HashMap<String, String>
var props = com.toolsverse.config.SystemConfig.instance().getProperties();
props.put("unique key", someValue);

Get

JavaScript
var props = com.toolsverse.config.SystemConfig.instance().getProperties();
var someValue = props.get("unique key");

Inside parallel loops or transformations

Parallel execution gets its own thread-safe context. Use getContextProperties() instead of getProperties(): values are visible only to the current ETL thread, with falls-back to the main thread on read:

JavaScript
var props = com.toolsverse.config.SystemConfig.instance().getContextProperties();
props.put("unique key", value);
// ...
var value = props.get("unique key");
// If not found in current ETL thread, returns whatever the main thread set.

Reference from SQL or connection params

Inside a Source / Destination query, or inside connection parameters: {global variable name}. Works for both main-thread and parallel-context values.

Flow variables

Per-flow key/value pairs. Set as URL parameters in user-defined APIs (see User APIs → Path parameters) or added by the user as parameters in a nested flow. Strings only.

Read

JavaScript
var value = scenario.getVariable("MY_VAR").getValue();

Write to an existing variable

JavaScript
scenario.getVariable("MY_VAR").setValue("new value");

Add a new variable at runtime

JavaScript
var v = new com.toolsverse.etl.common.Variable();
v.setName("MY_VAR");
v.setValue("initial value");
scenario.addVariable(v);

Reference from SQL or connection params

Inside a Source / Destination query, or inside connection parameters: {flow variable name}.

Flow key/value storage

Unlike global and flow variables (string-only), etlConfig's key/value storage holds any object: useful for caching UniqueNumber generators, parsed documents, lookup datasets, anything serializable. Scope is the current flow execution.

JavaScript
// Store
etlConfig.setValue("unique key", someObject);

// Retrieve later in the same flow
var obj = etlConfig.getValue("unique key");

Logging

Two ways. etlConfig.log(...) is the everyday choice: entries land in the flow's run log:

JavaScript
etlConfig.log("processed " + count + " rows");

For severity-tagged logging, use the JVM logger directly:

JavaScript
com.toolsverse.util.log.Logger.log(
  com.toolsverse.util.log.Logger.SEVERE,
  null,
  "Error doing something"
);

Best practices

When NOT to use scripting

Inline JavaScript / Python isn't the right tool for:

For Python-driven orchestration (replacing Airflow's PythonOperator / BashOperator), the Execute Script flow type runs Python 3, Bash, PowerShell, Node, Docker, cloud CLIs, SQL CLIs: locally or via SSH.