# User Defined Java Class

You can use the User Defined Java Class step to provide a custom Java class that drives the behavior of the step.

This step is not intended for full-scale Java plugin development. Instead, it lets you define Java methods and logic directly in the transformation.

The step uses the [Janino](https://janino-compiler.github.io/janino/apidocs/) libraries to compile Java code at runtime.

### How the step compiles your code

Janino (and this step) does not require a full Java class declaration. You provide the class body (for example, imports, constructors, and methods).

Your main code lives in the **Processor** tab, which defines the `processRow()` method.

The following imports are available in the Processor code by default:

* `org.pentaho.di.trans.steps.userdefinedjavaclass.*`
* `org.pentaho.di.trans.step.*`
* `org.pentaho.di.core.row.*`
* `org.pentaho.di.core.*`
* `org.pentaho.di.core.exception.*`

If you add additional class code tabs, those default Processor imports do not automatically apply.

If you need other imports, add them at the top of your Processor code, for example:

```java
import java.util.*;
```

Janino supports a subset of the Java 1.8.x specification. For details, see the [Janino homepage](http://janino-compiler.github.io/janino/).

### General

![User Defined Java Class step](https://773338310-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYwnJ6Fexn4LZwKRHghPK%2Fuploads%2Fgit-blob-95a4d4af64a6f20a86cb41eb5deb58850177893b%2FPDITransStep_UserDefinedJavaClass_PropertiesDialogBox.png?alt=media)

* **Step name**: Specify the unique name of the step on the canvas. You can customize the name or leave it as the default.

Use the **Class code** panel and option tabs to define your class. Select **Test class** to validate the class.

### Class code panel

![Class code panel](https://773338310-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYwnJ6Fexn4LZwKRHghPK%2Fuploads%2Fgit-blob-4a69340617a11cd8d8dfd5216dc808b737e06ee2%2FPDITransStep_UserDefinedJavaClass_ClassCode.png?alt=media)

Add your Java code in the **Processor** tab. You can add additional tabs by right-clicking and selecting **Add new**.

### Processor (processRow)

The Processor defines the `processRow()` method. The transformation calls this method repeatedly until it returns `false`.

{% hint style="info" %}
Call `getRow()` before the first `get(Fields.In, FIELD_NAME)` call. This helps avoid unexpected field-order issues (for example, with Mapping Input Specification).
{% endhint %}

Example Processor code (concatenate first name and last name):

```java
String firstnameField;
String lastnameField;
String nameField;
 
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException
{
    // Let's look up parameters only once for performance reason.
    //
    if (first) {
      firstnameField = getParameter("FIRSTNAME_FIELD");
      lastnameField = getParameter("LASTNAME_FIELD");
      nameField = getParameter("NAME_FIELD");
      first=false;
    }
 
    // First, get a row from the default input hop
    //
    Object[] r = getRow();
 
    // If the row object is null, we are done processing.
    //
    if (r == null) {
      setOutputDone();
      return false;
    }
 
    // Ensure output row is large enough for new fields.
    //
    Object[] outputRow = createOutputRow(r, data.outputRowMeta.size());
 
    String firstname = get(Fields.In, firstnameField).getString(r);
    String lastname = get(Fields.In, lastnameField).getString(r);
 
    // Set the value in the output field
    //
    String name = firstname+" "+lastname;
    get(Fields.Out, nameField).setValue(outputRow, name);
 
    // Send the row to the next step
    //
    putRow(data.outputRowMeta, outputRow);
 
    return true;
}
```

### Error handling

To route errors to an error hop:

1. Right-click the step on the canvas.
2. Select **Error handling**.
3. Configure the target step and field names.

Example (from `User Defined Java Class – Lambda Examples.ktr`):

```java
try {

Object     numList = strsList.stream()
                        .map( new ToInteger() )
                     .sorted( new ReverseCase() )
                     .collect( Collectors.toList() );

    get( Fields.Out, "reverseOrder" ).setValue( row, numList.toString() );

} catch (NumberFormatException ex) {
    // Number List contains a value that cannot be converteds to an Integer.
    rowInError = true;
    errMsg = ex.getMessage();
    errCnt = errCnt + 1;
}

if ( !rowInError ) {
    putRow( data.outputRowMeta, row );
} else {
    // Output errors to the error hop. Right click on step and choose "Error Handling..."
    putError(data.outputRowMeta, row, errCnt, errMsg, "Not allowed", "DEC_0");
}
```

### Logging

If you want PDI to log actions from your code, add logging logic.

Example:

```java
putRow( data.outputMeta, r );

if ( checkFeedback( getLinesOutput() ) ) {
  if ( log.isBasic() ) {
    logBasic( "Have I got rows for you! " + getLinesOutput() );
  }
}
```

### Classes and code fragments panel

![Classes and code fragments panel](https://773338310-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYwnJ6Fexn4LZwKRHghPK%2Fuploads%2Fgit-blob-8a2313297fe99308bb38417840987d5a22d82121%2FPDITransStep_UserDefinedJavaClass_ClassesAndCodeFragments.png?alt=media)

Use the **Classes and Code Fragments** panel to navigate your class code and related snippets.

* **Classes**: Shows which classes have corresponding code tabs.
* **Code snippets**: Shows internal PDI reference snippets.
* **Input fields** / **Info fields** / **Output fields**: Shows fields referenced or defined by your code.

Example: get input row metadata:

```java
RowMetaInterface inputRowMeta = getInputRowMeta();
```

For more details about PDI row data, see [PDI Rows Of Data](https://wiki.pentaho.com/display/EAI/PDI+Rows+Of+Data).

### Fields tab

![Fields tab](https://773338310-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYwnJ6Fexn4LZwKRHghPK%2Fuploads%2Fgit-blob-ab044990455f170197cb34c56627d5e70b766090%2FPDITransStep_UserDefinedJavaClass_FieldsTab.png?alt=media)

Use the **Fields** table to define output fields passed to downstream steps. Fields you define appear under **Output fields** in the **Classes and Code Fragments** panel.

### Parameters tab

![Parameters tab](https://773338310-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYwnJ6Fexn4LZwKRHghPK%2Fuploads%2Fgit-blob-a8a7a4e34e2f7d0b6d3c738158b50702dc6a1d4b%2FPDITransStep_UserDefinedJavaClass_ParametersTab.png?alt=media)

Use the **Parameters** table to avoid hard-coded strings (for example, field names).

Example:

```
getParameter("YEAR")
```

#### Class member variables and getVariable

If you call `getVariable()` in the `init()` method, variables resolve as expected. If you call it in a class member initializer, it does not resolve by design.

Example:

```java
private final String par = getVariable("somePar"); // DOES NOT resolve correctly
private String par2 = null;
 
public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {
   logBasic("Parameter value="+par+"[MEMBER INIT]");
   logBasic("Parameter value="+par2+"[INIT FUNCTION]");
   setOutputDone();
   return false;
}
 
public boolean init(StepMetaInterface stepMetaInterface, StepDataInterface stepDataInterface) {
   par2 = getVariable("somePar"); // WORKS FINE
   return parent.initImpl(stepMetaInterface, stepDataInterface);
}
```

### Info steps tab

![Info steps tab](https://773338310-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYwnJ6Fexn4LZwKRHghPK%2Fuploads%2Fgit-blob-1903cfacbea153ee808e54387d9eace50ea8c1e4%2FPDITransStep_UserDefinedJavaClass_InfoStepsTab.png?alt=media)

Because `getRow()` returns the first row from any input stream (input or info), configure **Info steps** when input and info row metadata differ.

Read info rows before calling `getRow()`.

Example:

```java
if (first){
 first = false;
 
 /* TODO: Your code here. (Using info fields)
 
 FieldHelper infoField = get(Fields.Info, "info_field_name");
 
 RowSet infoStream = findInfoRowSet("info_stream_tag");
 
 Object[] infoRow = null;
 
 int infoRowCount = 0;
 
 // Read all rows from info step before calling getRow() method.
 while((infoRow = getRowFrom(infoStream)) != null){
 
   // do something with info data
   infoRowCount++;
 }
 */
}
 
Object[] r = getRow();
 
if (r == null) {
       setOutputDone();
       return false;
}
```

### Target steps tab

![Target steps tab](https://773338310-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYwnJ6Fexn4LZwKRHghPK%2Fuploads%2Fgit-blob-4aeebc797b2d02460700fbf115fa0a1224785f9a%2FPDITransStep_UserDefinedJavaClass_TargetStepsTab.png?alt=media)

Use the **Target steps** table to route step output to specific steps in your transformation.

### Examples

The `data-integration/samples/transformations` directory contains these examples:

* `User Defined Java Class - Calculate the date of Easter.ktr`
* `User Defined Java Class - Concatenate firstname and lastname.ktr`
* `User Defined Java Class - Query the database catalog.ktr`
* `User Defined Java Class - Real-time search on Twitter.ktr`
* `User Defined Java Class - LambdaExamples.ktr`

We recommend starting with `User Defined Java Class - Calculate the date of Easter.ktr`.

### Metadata injection support

All fields of this step support metadata injection. You can use this step with [ETL metadata injection](https://docs.pentaho.com/pdia-data-integration/pdi-transformation-steps-reference-overview/etl-metadata-injection) to pass metadata to your transformation at runtime.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.pentaho.com/pdia-data-integration/pdi-transformation-steps-reference-overview/user-defined-java-class.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
