Skip to content
Connect2id

How to implement a Service Provider Interface (SPI) and package a JAR

This guide explains how to develop and package Java plugins for the Connect2id server.

The server features a set of plugin interfaces, referred to
as Service Provider Interfaces (SPI) in Java. An SPI is a standard Java mechanism for dynamically loading implementations of an interface at runtime. The plugin implementation is typically packaged in a JAR file.

1. Set up your project

Create a new Java project with the appropriate packaging, Java version and dependencies.

Packaging

The project must produce a JAR package.

Java version

Must not exceed the Java runtime version required by the Connect2id server. At the time of writing, this is Java 17.

Dependencies

Import the following dependencies:

<dependencies>
    <dependency>
        <!-- The Connect2id server SDK -->
        <groupId>com.nimbusds</groupId>
        <artifactId>c2id-server-sdk</artifactId>
        <version>[version]</version>
    </dependency>
    <dependency>
        <!-- The OAuth 2.0 / OpenID Connect SDK -->
        <groupId>com.nimbusds</groupId>
        <artifactId>oauth2-oidc-sdk</artifactId>
        <version>[version]</version>
    </dependency>
    <dependency>
        <!-- If the plugin needs to log messages -->
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>[version]</version>
    </dependency>
    <dependency>
        <!-- SPI annotation -->
        <groupId>org.kohsuke.metainf-services</groupId>
        <artifactId>metainf-services</artifactId>
        <version>1.11</version>
        <optional>true</optional>
    </dependency>
</dependencies>

The [version] values should match those used by the targeted minimum Connect2id server version.

How to determine out the dependency versions?

Inspect the SBOM file (XML or JSON) included in the c2id.war package (a ZIP archive), located under:

/WEB-INF/sbom

The project will also pull several transitive dependencies, such as those from the Nimbus JOSE+JWT library.

Shading dependencies to prevent conflicts

If the plugin depends on libraries that conflict with those bundled in the Connect2id server (for example, due to version or classpath clashes), those dependencies must be shaded.

The recommended tool is the Maven Shade Plugin.

Example using Gson:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.13.1</version>
</dependency>

The plugin configuration specifies the artifact to shade and the relocation of its package(s). The relocation should typically be to the plugin’s own package namespace.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.6.2</version>
    <configuration>
        <artifactSet>
            <includes>
                <include>com.google.code.gson:gson</include>
            </includes>
        </artifactSet>
        <relocations>
            <relocation>
                <pattern>com.google.gson</pattern>
                <shadedPattern>org.myplugin.shaded.gson</shadedPattern>
            </relocation>
        </relocations>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
</plugin>

To shade multiple dependencies add as many artifactSet.includes.include and relocations.relocation elements as necessary.

Dependency injection

Plugins may use dependency injection frameworks internally.

Be aware that shading can interfere with classpath scanning. If issues occur, configure the DI framework to use the relocated (shaded) package names.

2. Development

Implement the required SPI from the Connect2id server SDK.

The SDK JavaDocs provide detailed and up-to-date documentation.

Git repohttps://bitbucket.org/connect2id/server-sdk

Annotate the implementation class with @MetaInfServices to automatically generate the SPI manifest file and have it included in the JAR.

If the SPI manifest is missing, the plugin will not be loaded!

Example:

package com.example.my.c2id.plugin;

import org.kohsuke.MetaInfServices;
import com.nimbusds.openid.connect.provider.spi.claims.ClaimsSource;

@MetaInfServices
public class MyClaimsSource implements ClaimsSource {

    // Implementing code
}

This generates the SPI manifest file:

/META-INF/services/com.nimbusds.openid.connect.provider.spi.claims.ClaimsSource

With the content:

com.example.my.c2id.plugin.MyClaimsSource

3. Deployment

Copy the plugin JAR into:

/WEB-INF/lib/

of the Connect2id server deployment.

If deploying as a WAR file (e.g. c2id.war), add the plugin JAR to this directory inside the archive.

4. Troubleshooting

Plugin loading logs

The Connect2id server logs each detected plugin with a unique code defined in the SPI documentation. This makes it easy to verify successful loading in the server logs.

Example:

2017-08-07T13:39:44,634 INFO localhost-startStop-1 MAIN - [OP7101] Loaded claims source [1]: class com.nimbusds.openid.connect.provider.spi.claims.ldap.LDAPClaimsSource
2017-08-07T13:39:44,635 INFO localhost-startStop-1 MAIN - [OP7102] Claims supported by source [1]: sub email_verified address x_employee_number name nickname phone_number_verified phone_number preferred_username given_name family_name email

Single vs multiple enabled plugins for an SPI

Some Connect2id server SPIs support only a single active plugin, while others support multiple. This is specified in the SPI documentation.

If for a given SPI multiple plugins are enabled, but the SPI expects at most one enabled plugin, the Connect2id server aborts startup with an error.

Logging

The Connect2id server uses the Log4j framework. Plugins can use Log4j to emit operational and diagnostic logs.