Custom connector classes and bundles

Last modified 14 Sep 2026 07:59 UTC
Since 4.11
This functionality is available since version 4.11.

Most connectors can be built without any Java code, just a manifest and scripts. This page covers both the manifest-based route and the Java subclass route.

This article is part of the SQL connector development reference and guidance materials. See How to develop connectors using the SQL framework for the section introduction.

Option 1: manifest-based connector (no Java)

ManifestBasedConnector reads connector.manifest.json / .yaml / .yml from the classpath of the connector bundle (the bundle JAR together with the JARs in its lib/ directory) and loads the declared schema and operation scripts (see Declarative YAML).

Bundle layout:

my-sql-connector/
└── src/main/resources/
    ├── connector.manifest.json
    ├── Messages.properties
    ├── schema/
    │   └── users.groovy
    └── handlers/
        └── user_ops.groovy
{
  "connector": {
    "schema": [
      { "script": "/schema/users.groovy" }
    ],
    "operation": [
      { "script": "/handlers/user_ops.groovy" }
    ]
  }
}

The connector class is com.evolveum.polygon.sql.base.groovy.impl.ManifestBasedConnector. The generic SQL bundle in the repository ships with an empty manifest — add your scripts and rebuild, or reference the base bundle from your own assembly.

Notes:

  • Exactly one manifest may be bundled (connector.manifest.json, .yaml or .yml) — bundling more than one is a packaging error.

  • A script entry may carry "disabled": true — the script is skipped in both real loading and script validation, as if it were not bundled at all. Useful to toggle a script without repackaging.

  • Script paths are classpath resources; a leading / means the classpath root.

Option 2: custom connector class (Java)

For programmatic control, use subclass AbstractGroovySqlConnector:

package com.example;

import com.evolveum.polygon.sql.base.AbstractGroovySqlConnector;
import com.evolveum.polygon.sql.base.SqlConnectorConfiguration;
import com.evolveum.polygon.sql.base.groovy.SqlHandlerLoader;
import com.evolveum.polygon.sql.base.groovy.SqlSchemaDefinitionLoader;

public class ExampleSqlConnector extends AbstractGroovySqlConnector<SqlConnectorConfiguration> {

    public ExampleSqlConnector() {
        super(false);  // production: initialize once per instance
    }

    @Override
    protected void initializeSchema(SqlSchemaDefinitionLoader loader) {
        loader.loadFromResource("/schema/users.groovy");
        // loadFromResource resolves .yaml/.yml when the .groovy file is missing
    }

    @Override
    protected void initializeObjectClassHandler(SqlHandlerLoader loader) {
        loader.loadFromResource("/handlers/user_ops.groovy");
        // loader.loadFromString(...) and loader.register(objectClass, operationType, operation)
        // are available for fully programmatic handler registration
    }
}

Notes:

  • constructor argument reinitializeOnEachCall — use true in development (re-runs discovery and script loading on every call so script edits are picked up), and false in production (pool and handlers are initialized once). The manifest-based ManifestBasedConnector always uses false.

  • When the configuration is incomplete (no jdbcUrl/username or no password), schema() builds the schema from the bundled scripts alone — no connection pool is created and no discovery runs. This is useful for wizards and tooling that introspect a connector before a connection is configured.

  • operation scripts share a single Groovy shell, so helper functions defined in one script are available to later scripts:

    // /handlers/shared.groovy -- loaded first
    def activeUsers(int status) {
        return { e -> e.col("status").eq(status) }
    }

Java extension points

For behavior that goes beyond the DSL, the framework exposes programmatic hooks:

  • Custom mapping rulesSqlSchemaTranslator.addResourceRule(…​) and addAttributeRule(…​) register additional schema-mapping rules into the rule chain that runs after the built-in rules (see built-in mapping rules).

  • Fully custom operations — the per-object-class operation support builder accepts register(Class<T> operationType, T operation) to register a custom operation implementation; SqlHandlerLoader also provides loadFromString(…​) and register(objectClass, operationType, operation) for programmatic handler registration.

  • Script loadingSqlSchemaDefinitionLoader.loadFromResource(…​) resolves a .yaml/.yml document when the referenced .groovy file is missing from the bundle.

Script validation from the management system

In development mode (developmentMode true), the connector supports the ConnId runScriptOnResource SPI hook: the management system can submit a candidate Groovy script with the operation build or compile, and the connector validates it by evaluating the script against all sibling deployed scripts (the candidate replaces its own previous content; cross-references between scripts resolve). The validation result — compile errors or the built schema — is returned to the caller.

  • The hook is only enabled with developmentMode true; outside development mode it fails with UnsupportedOperationException.

  • Only groovy scripts and the build / compile operations are supported.

Packaging the bundle

The connector is packaged as a Java archive with all runtime dependencies in lib/:

mvn clean package
target/my-sql--connector*.jar
├── connector.manifest.json
├── Messages.properties
├── schema/...
├── handlers/...
└── lib/
    ├── (sql framework base jar)
    ├── hikariCP
    ├── groovy
    ├── querydsl-sql
    └── your JDBC driver (e.g., postgresql-*.jar)

Make sure the JDBC driver for your database is in the dependency set (the SQL framework itself does not bundle drivers). The Messages.properties file (at the root of the bundle, next to the manifest) provides the display labels for the configuration form: each entry maps a configuration property name (for example jdbcUrl) to the label shown on the midPoint connector form, so the form displays human-readable names instead of raw property keys.

Troubleshooting

Issue Solution

Schema is empty

Check scanTables/scanViews are true, that the connection user can read DatabaseMetaData, and that onlyExplicitlyListed is not hiding columns. For H2, use jdbc:h2:mem:dbname;DB_CLOSE_DELAY=-1 to keep the in-memory database alive. See Connector configuration.

Connection test failed

Verify jdbcUrl, username, password. Check that the driver JAR is in the bundle.

No object class for a table

Check the scanExcludeTables / scanExcludeViews patterns and the system-schema skips. See SQL schema discovery.

Scripts not loading

The script path in the manifest must match the classpath resource exactly (/ = classpath root). A missing .groovy silently falls back to .yaml/.yml.

Operation …​ is not supported for …​

The object class is read-only (view or readOnly true) or the operation was disabled — see SQL create operation, SQL update operation, SQL delete operation.

Groovy script fails at runtime

Script errors are wrapped with the resource name; review connector logs. In development mode you can validate a script from the management system before deployment (see above).

Was this page helpful?
YES NO
Thanks for your feedback