Custom Value Processors – Reference

ON THIS PAGE

API Overview

The custom value processor API lives in the com.basis.startup.type.sql package, which is available in BBjStartup.jar. This JAR must be on the compile-time classpath of any project that implements a custom processor. At runtime, the BBj SQL engine uses its own class loader to load your processor class, so you do not need to and should not re-bundle BBjStartup.jar inside your own JAR.

Type Package Role
ValueProcessor (abstract class) com.basis.startup.type.sql Base class you extend to create a custom processor.
BBjSQLLiteral (interface) com.basis.startup.type.sql Wraps an SQL literal value passed to computeRawValue(); provides typed accessors.
ValueProcessorBBjSQLFactory (interface) com.basis.startup.type.sql Factory interface your class implements to advertise your processors to the BBj SQL engine. Also extends BBjSQLFactory, making it the single class you need to register with the database.

The ValueProcessor Abstract Class

Extend com.basis.startup.type.sql.ValueProcessor and provide implementations of all six abstract methods described below. Your class must also expose a public default (no-argument) constructor because the SQL engine instantiates it via reflection.

computeReturnedValue(Object p_value)

public abstract Object computeReturnedValue(Object p_value) throws SQLException

Called by the SQL engine each time a row is read from the data file. The engine passes the raw value parsed from the string template, and your implementation must return the converted value in the Java type that corresponds to the SQL type declared by getSqlType().

Parameter Type Description
p_value Object The raw value from the data file after string-template parsing. For a character field (C type) this will be a String. For a numeric field (N, I, etc.) this will be a Number subtype. May be null for empty or missing fields.

Returns: The converted value. The runtime type must match the SQL type returned by getSqlType(). For example:

  • If getSqlType() returns Types.BOOLEAN, return a Boolean object.
  • If getSqlType() returns Types.DATE, return a java.sql.Date object.
  • Return null to represent a SQL NULL value.

Throws: java.sql.SQLException if the raw value cannot be converted.

computeRawValue(BBjSQLLiteral p_value)

public abstract Object computeRawValue(BBjSQLLiteral p_value) throws SQLException

Called by the SQL engine when a row is being written (via INSERT or UPDATE). The engine passes the SQL-typed value provided in the statement, and your implementation must return the raw value in the native form that matches the underlying string template field.

Parameter Type Description
p_value BBjSQLLiteral The SQL literal value provided by the INSERT or UPDATE statement. Use the typed accessor methods on BBjSQLLiteral to retrieve the value in the most appropriate form (e.g., getBoolean(), getString()); value may be null.

Returns: The raw value to be written to the data file. The type must match what the underlying string template field expects. For a C(1) field, return a String. For a numeric field, return a Number.

Throws: java.sql.SQLException if the value cannot be converted.

getSqlType()

public abstract int getSqlType();

Returns the JDBC SQL type constant (from java.sql.Types) that represents the logical data type this processor produces. The SQL engine uses this value to:

  • Report the column type via JDBC metadata (e.g., ResultSetMetaData.getColumnType()).
  • Determine how to handle type coercions in SQL expressions.
  • Override the native type stored in the type definition's sqlType field.

Common return values include Types.BOOLEAN, Types.DATE, Types.INTEGER, and Types.VARCHAR. See java.sql.Types Quick Reference below.

getDisplayName()

public abstract String getDisplayName()

Returns a short, human-readable name for this processor. This name appears in the Enterprise Manager type definition editor, in the dropdown list where administrators select which processor to assign to a column. Choose a name that clearly identifies what the processor does.

Example: Boolean Y/N Processor

getDescription()

public abstract String getDescription()

Returns a short description that provides additional context about what the processor does. This text is shown alongside the display name in Enterprise Manager to help administrators choose the correct processor.

Example: Converts string values Y and N to boolean true and false values respectively.

isLexicallyOrdered()

public abstract boolean isLexicallyOrdered()

Tells the SQL engine whether the raw values stored on disk sort in the same order as the processed values returned by computeReturnedValue(). When this returns true, the engine knows it can use an existing index on the raw data to optimize range queries (e.g., WHERE date_col > '2024-01-01').

Return true only when you are certain the sort orders are equivalent. Returning true incorrectly will produce wrong query results for range queries that rely on index optimization.

For boolean value processors (where the raw values are typically just two distinct values), it is generally safe to return true.

The BBjSQLLiteral Interface

The BBjSQLLiteral interface wraps a single SQL literal value that is passed to computeRawValue(). It provides typed accessors so you can retrieve the value in the most convenient form for your conversion. All accessors that involve type conversion may throw SQLException if the literal cannot be coerced to the requested type.

Method Returns Notes
isNull() boolean Returns true if the literal is SQL NULL.
getString() String String representation of the value.
getBoolean() boolean Boolean representation. Throws SQLException if conversion fails.
getByte() byte Byte representation.
getShort() short Short integer representation.
getInt() int Integer representation.
getLong() long Long integer representation.
getFloat() float Float representation.
getDouble() double Double representation.
getBigDecimal() BigDecimal BigDecimal representation.
getDate() java.sql.Date Date representation.
getTime() java.sql.Time Time representation.
getTimestamp() java.sql.Timestamp Timestamp representation.
getObject() Object The underlying object in its natural type.

The ValueProcessorBBjSQLFactory Interface

To make your custom value processor visible to the BBj SQL engine, you must also provide an implementation of the com.basis.startup.type.sql.ValueProcessorBBjSQLFactory interface. This interface extends BBjSQLFactory, which means your factory class is the single point of registration for both custom value processors and (optionally) custom scalar/group functions.

The interface declares one method:

Method Returns Purpose
getValueProcessors() Collection<ValueProcessor> Return a collection containing one instance of every ValueProcessor implementation this factory provides. The SQL engine calls this method to populate the processor list shown in the Enterprise Manager type definition editor.

Implementation Requirements

  • Public default constructor. The BBj SQL engine instantiates your factory class using reflection, so it must have a public no-argument constructor.
  • Implements ValueProcessorBBjSQLFactory. The class must explicitly declare implements ValueProcessorBBjSQLFactory. Implementing only BBjSQLFactory is not sufficient — the engine checks specifically for the ValueProcessorBBjSQLFactory sub-interface when discovering processors.
  • Package in the same JAR as your processors. The factory and all processor classes it references must be in the same JAR that is registered via the SQL_FACTORY_SSCP database property.

Minimal Example

The following factory exposes a single custom processor. The fully-qualified class name of this factory class is what you enter in the database's SQL_FACTORY property in Enterprise Manager.

package com.example.sql.valueprocessor;

import com.basis.startup.type.sql.ValueProcessor;
import com.basis.startup.type.sql.ValueProcessorBBjSQLFactory;
import java.util.Collection;
import java.util.List;

public class MyProcessorFactory implements ValueProcessorBBjSQLFactory
{
    /**
     * Public default constructor required by the BBj SQL engine.
     */
    public MyProcessorFactory()
    {
    }

    /**
     * Return every ValueProcessor this factory provides.
     * The SQL engine calls this to populate the processor dropdown
     * in the Enterprise Manager type definition editor.
     */
    @Override
    public Collection<ValueProcessor> getValueProcessors()
    {
        return List.of(new NumericBooleanValueProcessor());
    }
}

Note: If you have multiple processors, return all of them from getValueProcessors():

return List.of(
    new NumericBooleanValueProcessor(),
    new MyOtherProcessor()
);

Relationship Between SQL_FACTORY and SQL_FACTORY_SSCP

Two database properties work together to wire your factory into a database:

Property EM Label What to Set
SQL_FACTORY BBjSQLFactory Implementation The fully-qualified class name of your factory, e.g. com.example.sql.valueprocessor.MyProcessorFactory.
SQL_FACTORY_SSCP Scalar/Group Function SSCP The name of the SSCP (Session Specific Classpath) entry that points to the JAR containing your factory and processor classes.

Both properties are found under the Custom Functionality category in the database properties view in Enterprise Manager. The engine loads the factory class from the classpath defined by SQL_FACTORY_SSCP, so both properties must be set for your processor to appear in the type definition editor and function at query time.

Implementation Requirements

  • Public default constructor. Your class must have a public, no-argument constructor. The SQL engine instantiates value processors using reflection and requires this constructor.
  • No instance state that changes after construction. A single instance of your processor may be used concurrently by multiple threads. Your class must be thread-safe. The simplest approach is to make all fields final and set during construction only.
  • Handle null and empty values defensively. The p_value parameter to computeReturnedValue() can be null, and the string representation of an empty field may be an empty string. Always check for these conditions before attempting conversion.
  • Throw SQLException on unrecoverable errors. If a value cannot be converted, throw a java.sql.SQLException with a descriptive message. Do not swallow exceptions silently.
  • Compile against BBjStartup.jar. The ValueProcessor and BBjSQLLiteral types are defined in BBjStartup.jar, located in the BBj installation directory. Do not ship this JAR in your own deployment.

Packaging and Deployment

Compile your ValueProcessor implementation(s) and your ValueProcessorBBjSQLFactory implementation and package them together in a single standard JAR file. The JAR must not re-bundle BBjStartup.jar or any other core BBj JARs.

Place your JAR in a location accessible to the BBj server machine — for example:

/usr/local/basis/lib/custom/myprocessors.jar

The exact location does not matter as long as BBj can reach it as a file path; you will register this path as an SSCP entry in the next section.

Configuring a Database to Use Your Processor

Once your JAR is in place you must register it with BBj and then link it to the appropriate database column. This is a five-step process performed in Enterprise Manager.

Step 1: Register the JAR as a Session Specific Classpath (SSCP)

An SSCP (Session Specific Classpath) is a named classpath entry managed by BBj. It tells BBj where to find external classes at runtime. You need one SSCP entry that points to your JAR.

  • Open Enterprise Manager and connect to your BBj server.

  • Navigate to Settings > Classpath (or the equivalent SSCP management section in your version of Enterprise Manager).

  • Create a new SSCP entry. Give it a meaningful name such as myProcessorsSSCP.

  • Add the path to your JAR file as an entry in the classpath, for example /usr/local/basis/lib/custom/myprocessors.jar.

  • Save the SSCP entry.

Step 2: Set the Database SQL_FACTORY Property

The SQL_FACTORY property (displayed in Enterprise Manager as BBjSQLFactory Implementation) tells the SQL engine which factory class to instantiate. Set it to the fully-qualified class name of your ValueProcessorBBjSQLFactory implementation.

  • In Enterprise Manager, expand the Databases tree and select the target database.
  • Open the database properties / configuration view.
  • Locate the field labelled BBjSQLFactory Implementation (SQL_FACTORY) under the Custom Functionality category.
  • Set its value to the fully-qualified class name of your factory, for example com.example.sql.valueprocessor.MyProcessorFactory.
  • Save the database configuration.

Step 3: Set the Database SQL_FACTORY_SSCP Property

Every BBj database has a property named SQL_FACTORY_SSCP (displayed in Enterprise Manager as Scalar/Group Function SSCP). This property tells the SQL engine which SSCP to use when loading external classes — including value processor classes — for that database.

  • In Enterprise Manager, expand the Databases tree and select the target database.
  • Open the database's properties / configuration view.
  • Locate the property labelled Scalar/Group Function SSCP (SQL_FACTORY_SSCP) under the Custom Functionality category.
  • Set its value to the name of the SSCP entry you created in Step 1, for example myProcessorsSSCP.
  • Save the database configuration.

Note: Both SQL_FACTORY and SQL_FACTORY_SSCP are also used by the custom scalar/group functions feature. If you are already using a custom BBjSQLFactory for scalar functions, you can extend that existing factory to also implement ValueProcessorBBjSQLFactory rather than creating a separate factory class. Both can coexist in the same JAR and the same SSCP.

Step 4: Assign the Processor to a Type Definition

A Type Definition (also called a custom type) is a named schema object in the BBj data dictionary that describes how a column's raw data should be interpreted. It can hold a reference to a value processor class. Type definitions are managed at the database level and can be reused across many columns and tables.

  • In Enterprise Manager, navigate to the target database and open the Types (or Type Definitions) section.
  • Create a new type definition or edit an existing one.
  • In the type definition editor, locate the Value Processor field. A dropdown list will show all value processors discovered for this database (built-in processors plus any found via the configured SSCP).
  • Select your processor from the list. The display name and description you implemented in getDisplayName() and getDescription() will be shown to help you identify the correct entry.
  • Save the type definition.

Step 5: Assign the Type Definition to a Column

Once a type definition with a value processor is defined, assign it to the specific column(s) whose raw data should be processed.

  • In Enterprise Manager, navigate to the target table and open its column editor.
  • Select the column whose data the processor should convert.
  • In the column's property editor, find the Type Definition (or Custom Type) field and select the type definition you configured in Step 4.
  • Save the table definition.

The processor is now active. Any SQL query that reads or writes that column will automatically invoke computeReturnedValue() and computeRawValue() respectively.

Important: Assigning a value processor to a column changes how the SQL engine reports the column's type in JDBC metadata (ResultSetMetaData.getColumnType()) and how it evaluates SQL expressions. Existing queries that compare the column to raw string or numeric literals (e.g., WHERE flag = 'Y') may need to be updated to use the processed type (e.g., WHERE flag = TRUE).

Built-In Processor Example: YNBooleanValueProcessor

The built-in YNBooleanValueProcessor is the simplest complete example of a value processor. It converts a one-character string field whose values are "Y" or "N" (or variants "T" / "F") to a SQL BOOLEAN. The full source is reproduced here for reference.

package com.basis.sql.valueprocessor;

import java.sql.SQLException;
import java.sql.Types;

import com.basis.startup.type.sql.BBjSQLLiteral;
import com.basis.startup.type.sql.ValueProcessor;

public class YNBooleanValueProcessor extends ValueProcessor
{
    @Override
    public Object computeReturnedValue(Object p_value) throws SQLException
    {
        String strVal = p_value == null ? null : p_value.toString();
        if (strVal == null || strVal.length() == 0)
        {
            return false;
        }
        else
        {
            strVal = strVal.toLowerCase();
            // "y..." or "t..." is true; everything else is false
            return strVal.startsWith("y") || strVal.startsWith("t");
        }
    }

    @Override
    public Object computeRawValue(BBjSQLLiteral p_value) throws SQLException
    {
        if (p_value == null)
        {
            return false;
        }

        String strVal = p_value.getString();
        // Boolean.parseBoolean() treats "true" (case-insensitive) as true
        return Boolean.parseBoolean(strVal) ? "Y" : "N";
    }

    @Override
    public int getSqlType()
    {
        return Types.BOOLEAN;
    }

    @Override
    public String getDisplayName()
    {
        return "Boolean Y/N Processor";
    }

    @Override
    public String getDescription()
    {
        return "Converts string values Y and N to boolean true and false values respectively.";
    }

    @Override
    public boolean isLexicallyOrdered()
    {
        return true;
    }
}

Key observations about this implementation:

  • computeReturnedValue() handles null and empty string defensively, treating them as false. It checks the first character only, accepting both "Y"/"y" (yes) and "T"/"t" (true) as truthy values.
  • computeRawValue() converts the SQL boolean back to the canonical "Y" or "N" string for storage.
  • getSqlType() returns Types.BOOLEAN so JDBC clients see an accurate column type.
  • isLexicallyOrdered() returns true because the field has only two possible values, making ordering irrelevant for range query optimization.

java.sql.Types Quick Reference

The following are the most commonly used java.sql.Types constants. Return the appropriate constant from getSqlType().

Constant Integer Value SQL Type Expected Java Return Type from computeReturnedValue()
Types.BOOLEAN 16 BOOLEAN java.lang.Boolean
Types.INTEGER 4 INTEGER java.lang.Integer
Types.BIGINT 5 BIGINT java.lang.Long
Types.NUMERIC 2 NUMERIC java.math.BigDecimal
Types.DOUBLE 8 DOUBLE java.lang.Double
Types.VARCHAR 12 VARCHAR java.lang.String
Types.DATE 91 DATE java.sql.Date
Types.TIME 92 TIME java.sql.Time
Types.TIMESTAMP 93 TIMESTAMP java.sql.Timestamp

See Also

SQL Custom Value Processors – Overview

Tutorial: Creating a Numeric Boolean Value Processor

java/sql/Types

Java Settings

Databases