Developing Java Extensions

Developing Java Extensions

Overview

Obviously the IdP is a Java application and it follows that extending it is often best handled using Java. While we provide an inordinate amount of scripting support, not everything can be done in scripts, and many things can but are much more complex to do with scripts. Coding in Java is often the best way to solve a problem, notwithstanding that Java extensions are typically going to require a more deliberate lifecycle to develop, test, install, and subsequently modify them.

An extension jar can be added easily to the IdP without requiring all of the additional conceptual overhead of the IdP’s plugin and module features. Those features are really crucial for managing extensions intended to be used by third parties but they are often overkill for a simple need to add a class or three to the IdP locally with no intent to distribute that extension.

A good developer ultimately understands how to pick the best tools for each job, and how to write code generally enough to avoid the need to constantly update it. Spring provides the huge advantage that you almost never need to develop any sort of custom code to configure an extension because it can invoke any of your classes' methods at configuration time to tailor a class to a specific use.

By far the hardest aspect of developing an extension is really just the joy that is Maven, and the need to juggle dependencies. The purpose of this information, and the companion poject provided, is to kickstart that step for you so you can focus on just authoring a little bit of code. The example ensures that you have a maintainable, buildable artifact.

Extension Skeleton

A skeletel extension project has been committed in https://codeberg.org/Shibboleth/java-idp-example-ext

This is not intended to be directly usable but can be turned into a local project for your own purposes with just a handful of changes:

  1. Pick a name. By design, it is constructed around the idea that you might just rename “example” in various places to your organization’s name. For the purposes of this example, let’s use “college”.

  2. Pick a Java package path. The example project use “org.example.shibboleth” as a generic path. If you were a university, and using our “college” example, then the path might be “edu.college.shibboleth”.

  3. Check out the code and update the project’s content and directories with the new names:

    git clone https://codeberg.org/Shibboleth/java-idp-example-ext.git mv java-idp-example-ext.git java-idp-college-ext.git cd java-idp-college-ext.git mv src/main/java/org src/main/java/edu mv src/main/java/edu/example src/main/java/edu/college
  4. Update the POM’s coordinates (very top of the pom.xml file in the root folder):
    OLD:

    <name>Example IdP Extensions</name> <groupId>org.example.shibboleth</groupId> <artifactId>idp-example-ext</artifactId>

    NEW:

    <name>College IdP Extensions</name> <groupId>edu.college.shibboleth</groupId> <artifactId>idp-college-ext</artifactId>
  5. Update the single example class' package designation. This is simply so that the project can build itself; you will very likely throw away the example class later. The example will be in src/main/java/edu/college/ExampleClass.java:
    OLD:

    package org.example.shibboleth;

    NEW:

    package edu.college.shibboleth;

With these changes, you should be able to build the project using a recent version of Maven:

mvn package

The package will be in target/idp-college-ext-1.0.0-SNAPSHOT.jar and contains merely a couple of example files.

Dependencies

The Maven POM contains an initial set of (mostly) commented dependencies, and a set of BOM imports at the bottom that pull in all of a particular version of the IdP’s entire stack. What that does is automatically control the versions of all of the different modules so that they stay in sync with each other.

Most of the POM as provided is really just a starter for you that sets up the basics so you can just stick to adding dependencies as you need them.

When you write code that relies on APIs from our projects, you will need to identify (usually via Javadoc) which module provides it and uncomment it so your code will build. Maven will report compilation errors to you (as will an IDE such as Eclipse) if the POM doesn’t include the dependencies needed. If the API is from a third party jar that’s not already listed for you, you’ll have to add it, but most projects will tell you what that looks like in Maven terms in their documentation.

All of the IdP’s libraries are what is called “provided” in Maven-speak, which means that they are already there for your code at runtime and don’t have to be added to the system. Any time you include a dependency on something that ships with the IdP or its plugins, it would be “provided” scope.

If in unusual cases you need to introduce a new dependency, it would be a compile-time scope (which is the default) and you would need to add it to the IdP’s warfile along with your own extension (see below).

Adding the Extension to the IdP

This is the simple part: just add your jar(s) to edit-webapp/WEB-INF/lib and re-run the IdP’s build.sh or build.bat script and it will add your libraries to the IdP’s warfile. You should NEVER include an actual module we supply with the IdP in that location as it would cause problems during future upgrades. This is only used to add “new” code to the system.

Of course, if you include a new third party dependency, you are taking the responsibility to ensure that it is kept up to date. You also need to consider whether it might share any transitive dependencies on anything we do provide because that can lead to conflicts if the new library you add requires an older or newer version of something we use but that may not be compatible. This is the Java version of DLL Hell.