# 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.
