Tutorial: Creating a Numeric Boolean Value Processor

In this tutorial you will build a complete, working value processor that converts a numeric column storing 1 (true) and 0 (false) into a proper SQL BOOLEAN type. The project is built with Apache Maven, which handles the compile-time dependency on BBjStartup.jar and produces a ready-to- deploy JAR in one command. By the end you will have:

  • Created the ORDERS table in the ChileCompany database using the Enterprise Manager SQL console.
  • Created a Maven project with the correct structure and pom.xml.
  • Written the ValueProcessor Java class.
  • Written the ValueProcessorBBjSQLFactory Java class.
  • Built the project and located the output JAR.
  • Registered the JAR with BBj as a Session Specific Classpath (SSCP).
  • Configured the database SQL_FACTORY and SQL_FACTORY_SSCP properties.
  • Assigned the processor to a column via a type definition in Enterprise Manager.
  • Verified the result with SQL queries.
Prerequisites
  • A working BBj installation with Enterprise Manager access.
  • The ChileCompany sample database configured and accessible in your BBj installation.
  • Apache Maven 3.9 or later installed on your development machine. Verify with mvn --version.
  • A Java Development Kit (JDK) 21 or later installed and on your PATH.
  • Access to BBjStartup.jar from your BBj installation directory (typically <BBj-install>/.lib/BBjStartup.jar). This JAR is needed at compile time only and is not bundled into your output.

STEPS IN THIS TUTORIAL

The Scenario

In this tutorial you will work with a table called ORDERS that has a column called IS_SHIPPED defined as a numeric field (NUMERIC(1)). The application stores 1 when an order has shipped and 0 when it has not. Without a value processor, a SQL query must compare against raw numbers:

SELECT ORDER_ID, IS_SHIPPED FROM ORDERS WHERE IS_SHIPPED = 1

This works, but it exposes the raw storage detail to every caller. After applying a value processor you will be able to use a proper SQL boolean literal instead:

SELECT ORDER_ID, IS_SHIPPED FROM ORDERS WHERE IS_SHIPPED = TRUE

The IS_SHIPPED column will be reported as BOOLEAN in JDBC metadata, and result set values will be true or false rather than 1 or 0. The first step below creates this table so you have a real target to configure the processor against.

Create the ORDERS Table

Before writing any Java code, create the table you will configure the value processor against. You will use the built-in SQL console in Enterprise Manager to run a CREATE TABLE statement directly against the ChileCompany database.

  • Open Enterprise Manager and connect to your BBj server.
  • In the left-hand navigation tree, expand Databases/SQL and select the Databases node.
  • Select the ChileCompany database from the list.
  • Select the SQL tab to open the SQL console.
  • Enter the following statement in the SQL input area and execute it:

CREATE TABLE ORDERS (ORDER_ID INTEGER PRIMARY KEY, IS_SHIPPED NUMERIC(1))

This creates the ORDERS table with two columns: an integer primary key and a single-digit numeric column that will store 1 (shipped) or 0 (not shipped). This is the column you will attach the value processor to in later steps.

✓ Result: The ORDERS table exists in the ChileCompany database and is ready to use.

Create the Maven Project Structure

Run the following command in a terminal to generate the project. Maven will prompt you to confirm the settings; press Enter to accept each default.

mvn archetype:generate \
  -DgroupId=com.example.sql.valueprocessor \
  -DartifactId=myprocessors \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DarchetypeVersion=1.5 \
  -DinteractiveMode=false

This creates a directory called myprocessors with a skeleton pom.xml and a sample Java source file.

Before continuing, delete both generated sample files — the tutorial's pom.xml does not include JUnit dependencies, so AppTest.java will fail to compile if left in place:

  • src/main/java/com/example/sql/valueprocessor/App.java
  • src/test/java/com/example/sql/valueprocessor/AppTest.java

You will replace App.java with your processor class in Step 4.

✓ Result: You have a Maven project root directory called myprocessors with the standard src/main/java source tree.

Install BBjStartup.jar into the Local Maven Repository

BBjStartup.jar is not published to Maven Central, so you need to install it into your local Maven repository (~/.m2/repository) manually. This only needs to be done once per development machine, or whenever you update your BBj installation.

Run the following command, replacing /path/to/BBj/.lib/BBjStartup.jar with the actual path on your system:

mvn install:install-file \
  -Dfile=/path/to/BBj/.lib/BBjStartup.jar \
  -DgroupId=com.basis \
  -DartifactId=BBjStartup \
  -Dversion=1.0 \
  -Dpackaging=jar

The values for -DgroupId, -DartifactId, and -Dversion are local identifiers that you choose. They only need to match what you reference in your pom.xml in the next step. The values shown here (com.basis:BBjStartup:1.0) are used throughout this tutorial.

Note: If multiple developers are building this project you can also deploy BBjStartup.jar to an internal Maven repository such as Nexus or Artifactory instead of installing it locally on each machine.

✓ Result: Maven can now resolve BBjStartup.jar as a compile-time dependency using the coordinates com.basis:BBjStartup:1.0.

Write the pom.xml

Replace the contents of myprocessors/pom.xml with the following. Each section is explained in the comments and in the table below.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             https://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <!-- ── Project identity ─────────────────────────────────────── -->
  <groupId>com.example.sql.valueprocessor</groupId>
  <artifactId>myprocessors</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>
  <name>My BBj Value Processors</name>

  <properties>
    <!-- Match the Java version required by your BBj installation. -->
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>

    <!-- ── BBjStartup.jar ────────────────────────────────────────
         Provides ValueProcessor and BBjSQLLiteral.
         scope=provided means Maven uses it for compilation only;
         it will NOT be bundled inside the output JAR.
         The coordinates here must match what you used in Step 3.
    ────────────────────────────────────────────────────────────── -->
    <dependency>
      <groupId>com.basis</groupId>
      <artifactId>BBjStartup</artifactId>
      <version>1.0</version>
      <scope>provided</scope>
    </dependency>

  </dependencies>

  <build>
    <!-- Controls the output JAR filename. -->
    <finalName>myprocessors</finalName>

    <plugins>

      <!-- Pin the JAR plugin version explicitly. -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.4.2</version>
      </plugin>

    </plugins>
  </build>

</project>

Key points about this POM:

Element Explanation
<groupId> / <artifactId> / <version> These identify your project within Maven. They can be anything you like; the values used here are just examples.

maven.compiler.source /

maven.compiler.target

Set these to match the Java version your BBj server runs. BBj requires Java 21 or higher. If you are unsure, check the BBj release notes for your version.
<scope>provided</scope> This is critical. It tells Maven that BBjStartup.jar will be provided by the runtime environment (BBj), so Maven should use it for compilation but must not include it in the output JAR. Omitting this scope would bundle BBj classes into your JAR and cause class-loader conflicts at runtime.

<build>

<finalName>myprocessors</finalName>

</build>

Controls the output JAR filename. Placed directly inside <build> (not inside a plugin <configuration>, where newer versions of the maven-jar-plugin treat it as read-only). Without this, Maven defaults to myprocessors-1.0.0.jar (artifactId + version). Setting a fixed name makes deployment scripts easier to write.

✓ Result: pom.xml is complete. Maven can now resolve the compile-time dependency and build the project.

Write the ValueProcessor Class

Create the file src/main/java/com/example/sql/valueprocessor/NumericBooleanValueProcessor.java inside your myprocessors project directory and paste in the following source:

package com.example.sql.valueprocessor;

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

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

/**
 * Converts a numeric column that stores 1 (true) or 0 (false)
 * to and from a SQL BOOLEAN value.
 *
 * Raw storage: N(1) field containing the integer 1 or 0.
 * SQL type:    BOOLEAN (java.lang.Boolean).
 */
public class NumericBooleanValueProcessor extends ValueProcessor
{
    /**
     * Public default constructor required by the BBj SQL engine,
     * which instantiates this class via reflection.
     */
    public NumericBooleanValueProcessor()
    {
        // No initialisation needed.
    }

    /**
     * Converts the raw numeric value from the data file to a Boolean.
     *
     * The raw value will be a Number (e.g. Integer, Double, BigDecimal)
     * for a numeric field.  Any non-zero value is treated as true;
     * zero, null, and empty are treated as false.
     */
    @Override
    public Object computeReturnedValue(Object p_value) throws SQLException
    {
        if (p_value == null)
        {
            return Boolean.FALSE;
        }

        String str = p_value.toString().trim();
        if (str.isEmpty())
        {
            return Boolean.FALSE;
        }

        try
        {
            return Integer.parseInt(str) != 0;
        }
        catch (NumberFormatException e)
        {
            throw new SQLException(
                "NumericBooleanValueProcessor: cannot convert value \""
                + str + "\" to a boolean.",
                e);
        }
    }

    /**
     * Converts a SQL BOOLEAN value back to the raw numeric form (1 or 0)
     * to be written to the data file.
     */
    @Override
    public Object computeRawValue(BBjSQLLiteral p_value) throws SQLException
    {
        if (p_value == null || p_value.isNull())
        {
            return Integer.valueOf(0);
        }

        return p_value.getBoolean() ? Integer.valueOf(1) : Integer.valueOf(0);
    }

    /**
     * Reports this column as a SQL BOOLEAN type to JDBC clients.
     */
    @Override
    public int getSqlType()
    {
        return Types.BOOLEAN;
    }

    /**
     * Name shown in Enterprise Manager when selecting a value processor.
     */
    @Override
    public String getDisplayName()
    {
        return "Boolean 1/0 Numeric Processor";
    }

    /**
     * Description shown alongside the display name in Enterprise Manager.
     */
    @Override
    public String getDescription()
    {
        return "Converts a numeric field storing 1 (true) or 0 (false) "
             + "to and from a SQL BOOLEAN value.";
    }

    /**
     * Returns true because the numeric values 0 and 1 sort in the same
     * order as false and true, so existing numeric indexes remain usable
     * for range query optimisation.
     */
    @Override
    public boolean isLexicallyOrdered()
    {
        return true;
    }
}

Walk-through of the key decisions in this implementation:

Decision Explanation
Check for Number first in computeReturnedValue() Numeric template fields deliver a Number object, not a String. Checking the concrete type avoids an unnecessary string conversion.
Treat null and empty as false Many legacy files leave fields at their default zero-initialized value. Mapping these to false is the safest default behaviour.
Return Integer.valueOf(0) / Integer.valueOf(1) from computeRawValue() The underlying field is a numeric (N) type, so the raw value written back to the file must be a Number, not a String.
isLexicallyOrdered() returns true 0 < 1 numerically, and false < true logically, so the sort order is consistent. The SQL engine can therefore use a numeric index on IS_SHIPPED for optimised range queries.

Your project directory should now look like this:


myprocessors/
├── pom.xml
└── src/
    └── main/
        └── java/
            └── com/
                └── example/
                    └── sql/
                        └── valueprocessor/
                            └── NumericBooleanValueProcessor.java

✓ Result:The project structure is complete and ready to build.

Write the ValueProcessorBBjSQLFactory Class

The BBj SQL engine does not automatically scan your JAR for processor classes at query time. Instead, it looks for a ValueProcessorBBjSQLFactory implementation whose class name is registered in the database's SQL_FACTORY property. This factory class is responsible for advertising your processor(s) to the engine.

Create a new file src/main/java/com/example/sql/valueprocessor/NumericBooleanProcessorFactory.java:

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;

/**
 * Factory that advertises NumericBooleanValueProcessor to the BBj SQL engine.
 *
 * The fully-qualified name of this class is what you enter in the
 * database's SQL_FACTORY property in Enterprise Manager:
 *   com.example.sql.valueprocessor.NumericBooleanProcessorFactory
 */
public class NumericBooleanProcessorFactory implements ValueProcessorBBjSQLFactory
{
    /**
     * Public default constructor required by the BBj SQL engine,
     * which instantiates this class via reflection.
     */
    public NumericBooleanProcessorFactory()
    {
    }

    /**
     * Returns all ValueProcessor implementations provided by this factory.
     * 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());
    }
}

Key points about this class:

Point Explanation
implements ValueProcessorBBjSQLFactory This is the specific sub-interface the engine checks for. Implementing only BBjSQLFactory is not sufficient — the engine uses ValueProcessorBBjSQLFactory to discover processor lists.
Public default constructor The engine instantiates this class using reflection and requires a public no-argument constructor.
List.of(new NumericBooleanValueProcessor()) Each call to getValueProcessors() returns fresh instances. If you add more processors in the future, add them here: List.of(new ProcessorOne(), new ProcessorTwo()).

Your project source tree should now look like this:

myprocessors/
├── pom.xml
└── src/
    └── main/
        └── java/
            └── com/
                └── example/
                    └── sql/
                        └── valueprocessor/
                            ├── NumericBooleanProcessorFactory.java
                            └── NumericBooleanValueProcessor.java

✓ Result: Both Java source files are in place. The project is ready to build.

Build the Project and Locate the JAR

From a terminal, change into the myprocessors project root directory (the directory that contains pom.xml) and run:

cd myprocessors
mvn clean package

Maven will compile the source, run any tests, and assemble the JAR. A successful build ends with output similar to:

[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------
[INFO] Total time:  3.142 s
[INFO] Finished at: 2026-03-03T10:00:00

Where to find the output JAR

Maven writes the output JAR to the target/ subdirectory inside your project root. Because you set <finalName>myprocessors</finalName> inside <build> in the POM, the file is named:

myprocessors/target/myprocessors.jar

You can verify the contents of the JAR to confirm no BBj classes were accidentally included:

jar tf target/myprocessors.jar

Expected output — only your class and Maven metadata should be listed:

META-INF/
META-INF/MANIFEST.MF
com/example/sql/valueprocessor/NumericBooleanValueProcessor.class

Important: If you see any com/basis/ entries in this listing, the BBj classes were accidentally bundled. Go back to Step 4 and confirm that the BBjStartup dependency has <scope>provided</scope>, then rebuild.

Note: If you want Maven to also copy the JAR to a specific output location automatically (for example, directly into a network share accessible to your BBj server), you can add the maven-resources-plugin or an Ant task to your POM. For most cases, manually copying the file as described in the next step is sufficient.

✓ Result: target/myprocessors.jar is ready to deploy.

Deploy the JAR to the BBj Server

Copy target/myprocessors.jar to a location on the BBj server machine that BBj can read. A common convention is a custom subdirectory under the BBj installation directory:

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

Note and record the full absolute path; you will need it when creating the SSCP entry in the next step.

Note: If your development machine is different from the BBj server, transfer the JAR via SCP, SFTP, or any other file transfer mechanism you normally use for deployments to that server.

Create an SSCP in Enterprise Manager

A Session Specific Classpath (SSCP) is a named classpath record that BBj uses to locate external classes at runtime. You need one SSCP that includes your JAR.

  • Open Enterprise Manager and log in to your BBj server.
  • In the left-hand navigation tree, expand BBjServices and select Java Settings.
  • Click add (the "+" button) to create a new classpath entry.
  • Enter a Name for the classpath. For this tutorial, use myprocessors.
  • Add a new classpath entry (click the "+" button in the upper right corner) and select JAR File(s) and then locate the JAR you deployed in Step 7:
    /usr/local/basis/lib/custom/myprocessors.jar
  • Click the Save button.

✓ Result:The SSCP myprocessors is registered with BBj and points to myprocessors.jar.

Set the Database SQL_FACTORY and SQL_FACTORY_SSCP Properties

Two database properties must be set to wire your factory into the database. Both are found under the Custom Functionality category in the database properties view.

  • In the Enterprise Manager navigation tree, expand Databases and select the database that contains your ORDERS table.

  • Select the Settings tab.

  • Find the Custom Functionality section in the properties panel.

  • Locate the field labelled BBjSQLFactory Implementation and set its value to the fully-qualified class name of your factory:

com.example.sql.valueprocessor.NumericBooleanProcessorFactory
  • Locate the field labelled Scalar/Group Function SSCP and set its value to the SSCP name you created in Step 9:

myprocessors
  • Click Save.

✓ Result: The SQL engine will load NumericBooleanProcessorFactory from myprocessors.jar via myprocessors, making NumericBooleanValueProcessor available for selection in the type definition editor.

Create a Type Definition

A Type Definition is a named schema object in the BBj data dictionary that can carry a value processor class reference. You will create one specifically for your numeric boolean column.

  • In Enterprise Manager, with your database selected, navigate to the Types tab.

  • Click add (the “+” button) to create a new type definition.

  • Set the following properties:

Property Value Notes
Name BOOLEAN_NUMERIC The internal name. Choose something meaningful and unique within the database.
Native Type I (Integer) Must match the underlying BBx string template field type of the column you will assign this to.
Value Processor Boolean 1/0 Numeric Processor Select from the dropdown. Your processor should appear here now that the SSCP is configured. The name shown comes from your getDisplayName() implementation.
Dictionary Length 1 Indicates that the data is 1 digit.
  • Click Save.

Note: If your processor does not appear in the Value Processor dropdown, check that:
  • BBjSQLFactory Implementation and Scalar/Group Function SSCP are both set correctly on the database (Step 10).

  • The SSCP entry points to the correct JAR path (Step 8).

  • The JAR file exists at that path and is readable by the BBj server process.

  • The class is actually inside the JAR: run jar tf myprocessors.jar to verify.

✓ Result: The type definition BOOLEAN_NUMERIC is defined in the database dictionary and references your NumericBooleanValueProcessor class.

Assign the Type Definition to the Column

Link the BOOLEAN_NUMERIC type definition to the IS_SHIPPED column in your ORDERS table.

  • On the Tables tab, double-click on the ORDERS table (you may need to click the refresh button to see the new table).
  • Select the Columns tab.
  • Select the IS_SHIPPED column in the column list.
  • In the column's properties panel, find the Data Type field and select BOOLEAN_NUMERIC from the dropdown.
  • Click Save.

✓ Result: The IS_SHIPPED column is now backed by your NumericBooleanValueProcessor. Every SQL read calls computeReturnedValue() and every SQL write calls computeRawValue().

Verify the Result

Select the SQL tab at the bottom of the page and run the following query to confirm the processor is working correctly.

Verify SELECT returns Boolean values

SELECT ORDER_ID, IS_SHIPPED
FROM   ORDERS

The IS_SHIPPED column should now return true or false instead of 1 or 0.

Verify filtering with Boolean literals

-- Find all orders that have shipped
SELECT ORDER_ID FROM ORDERS WHERE IS_SHIPPED = TRUE

-- Find all orders that have not shipped
SELECT ORDER_ID FROM ORDERS WHERE IS_SHIPPED = FALSE

Both queries should return correct results without any raw numeric comparisons.

Verify writing (UPDATE)

UPDATE ORDERS
SET    IS_SHIPPED = TRUE
WHERE  ORDER_ID = 1

After the UPDATE, re-select the row. IS_SHIPPED should return true. The underlying data file stores the integer 1, confirming that computeRawValue() converted the boolean correctly.

Troubleshooting tip: If queries return unexpected results, temporarily remove the type definition from the column and re-query to see the raw values. This lets you confirm what is stored on disk and trace any conversion issue back to the specific method in your processor.

✓ Tutorial complete. Your NumericBooleanValueProcessor is fully deployed and operational. The IS_SHIPPED column now behaves as a native SQL BOOLEAN from the perspective of every SQL client, while the underlying data file remains unchanged.

Summary of Steps

# Action Tool
1 Create the ORDERS table in ChileCompany via the EM SQL console Enterprise Manager
2 Create the Maven project directory structure mvn archetype:generate
3 Install BBjStartup.jar into the local Maven repository mvn install:install-file
4 Write pom.xml with provided-scope BBjStartup dependency Text editor / IDE
5 Write NumericBooleanValueProcessor.java Text editor / IDE
6 Write NumericBooleanProcessorFactory.java (ValueProcessorBBjSQLFactory) Text editor / IDE
7 Build with mvn clean package; output at target/myprocessors.jar mvn
8 Copy myprocessors.jar to the BBj server cp / file transfer
9 Create SSCP myprocessors pointing to the JAR Enterprise Manager
10 Set BBjSQLFactory Implementation and Scalar/Group Function SSCP on the database Enterprise Manager
11 Create type definition BOOLEAN_NUMERIC referencing the processor Enterprise Manager
12 Assign BOOLEAN_NUMERIC to the IS_SHIPPED column Enterprise Manager
13 Verify with SQL queries SQL console / JDBC client

See Also

SQL Custom Value Processors – Overview

Custom Value Processors – Reference

java/sql/Types

Java Settings

Databases