Crafting your ORDS plugin the Maven way

This article provides a step-by-step guide to setting up an Oracle REST Data Services (ORDS) plugin using Maven. It covers the installation of two ORDS jars into the local Maven repository, the creation of the project structure, the configuration of the plugin, and the building and deployment of the plugin. This guide will help developers get started quickly with ORDS plugin development where Maven is already used in their software development infrastructure.

  1. Introduction
  2. Step 1: Install ORDS Jars Into the Local Maven Repository
  3. Step 2: Set Up the Project Structure
  4. Step 3: Create Configuration Files and Source
  5. Step 4: Build and Deploy the Plugin
  6. Step 5: Verify the Plugin
  7. Conclusion

Introduction

ORDS provides a plugin framework which makes it possible to extend the functionality and introduce custom behaviour. Creating a plugin can be a daunting task, especially if you’re not familiar with ORDS or the underlying technologies. Fortunately, there are some great examples out there to get you started, including the ORDS YAML plugin example. However, the plugin example projects are Ant-based, so if you’re looking to use Maven for your plugin project, you’ll need to take a few extra steps.

These steps have Maven as a prerequisite and it is assumed your familiarity with Maven is the reason you’re here, reading this article. If you need more information on Maven then start with What is Maven?

Step 1: Install ORDS Jars Into the Local Maven Repository

An ORDS plugin has a dependency on two jars that are distributed with ORDS. The ords-plugin-api jar provides all the interfaces, annotations and base classes described in the ORDS Java API which are the building blocks for your plugin implementation. The ords-plugin-apt jar provides the annotation processing support required when building your plugin. These two jar files can be found in the examples/plugins/lib/ directory of ORDS distribution.

First, you’ll need to install the two ORDS jars into your local Maven repository. To install the jars, you’ll need to use the command line. Navigate to the directory where the ORDS was extracted to, and then run the following two commands:

> mvn install:install-file \
  -DgroupId=oracle.dbtools.ords \
  -DartifactId=ords-plugin-api \
  -Dversion=22.4.4 \
  -Dpackaging=jar \
  -Dfile=examples/plugins/lib/ords-plugin-api-22.4.4.041.1526.jar  \
  -DgeneratePom=true  

> mvn install:install-file \
  -DgroupId=oracle.dbtools.ords \
  -DartifactId=ords-plugin-apt \
  -Dversion=22.4.4 \
  -Dpackaging=jar \
  -Dfile=examples/plugins/lib/ords-plugin-apt-22.4.4.041.1526.jar  \
  -DgeneratePom=true

That will put two artefacts in your local maven repository both as version 22.4.4. That is the version of ORDS that this article refers to but you can change the version number to be reflect the version of ORDS you are using.

Step 2: Set Up the Project Structure

This guide is based on the ORDS YAML plugin example and will use the source PluginYaml.java from that article. The plugin can modify the application/json response which would normally be returned by ORDS and that response in text/yaml format instead.

For simplicity we’ll use maven-archetype-quickstart as outlined in Maven in 5 Minutes to create the project structure. The focus here is on creating an example, so that term makes sense as the group identifier. You should use your existing Maven artefact nomenclature for identifiers. In the directory where you typically have your Maven projects run the following command:

> mvn archetype:generate \
    -DgroupId=example \
    -DartifactId=ords-yaml-plugin \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DarchetypeVersion=1.4 \
    -DinteractiveMode=false


[INFO] Scanning for projects...
[INFO] 
[INFO] ---------< org.apache.maven:standalone-pom >----------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] -----------------------[ pom ]------------------------
[INFO] 
...
[INFO] ------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------
[INFO] Total time:  4.779 s
[INFO] Finished at: 2023-03-27T23:18:25+01:00
[INFO] ------------------------------------------------------

You should now have an ords-yaml-plugin directory which looks like this:

ords-yaml-plugin
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- example
    |           `-- App.java
    `-- test
        `-- java
            `-- example
                `-- AppTest.java

The generated App.java and AppTest.java files can be removed or ignored. They serve no purpose but to provide placeholders in the source directory. Change directory into ords-yaml-plugin and verify the project builds.

> cd ords-yaml-plugin 
> mvn clean package
[INFO] Scanning for projects...
[INFO] 
[INFO] -------------< example:ords-yaml-plugin >-------------
[INFO] Building ords-yaml-plugin 1.0-SNAPSHOT
[INFO] -----------------------[ jar ]------------------------
...
[INFO] ------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------
[INFO] Total time:  2.755 s
[INFO] Finished at: 2023-03-27T23:39:42+01:00
[INFO] ------------------------------------------------------

Once that completes you should see target/ords-yaml-plugin-1.0-SNAPSHOT.jar file has been produced. Let’s make it interesting by adding the PluginYaml.java to the project.

Step 3: Create Configuration Files and Source

To compile the ORDS plugin there are classpath dependencies that must be met. Modify the ords-yaml-plug project pom.xml and put these dependencies just after the <dependencies> element at line #21.

    <dependency>
      <groupId>oracle.dbtools.ords</groupId>
      <artifactId>ords-plugin-api</artifactId>
      <version>22.4.4</version>
    </dependency>
    <dependency>
      <groupId>oracle.dbtools.ords</groupId>
      <artifactId>ords-plugin-apt</artifactId>
      <version>22.4.4</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>jakarta.inject</groupId>
        <artifactId>jakarta.inject-api</artifactId>
        <version>2.0.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.14.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-yaml</artifactId>
        <version>2.14.2</version>
    </dependency>

Most of those dependencies will be satisfied by the artefacts already available in the central Maven repository and you have addressed the remaining two dependencies by putting the ORDS plugin api and apt jars in your local repository earlier.

Download the source for the plugin class into the source package directory created when the Maven project was created.

> curl --output src/main/java/example/PluginYaml.java \
       https://raw.githubusercontent.com/pobalopalous/pobalopalous/main/example/PluginYaml.java

That PluginYaml.java example was written for an earlier version of ORDS which used the javax.inject library. ORDS 22.4.4 uses the jakarta.inject library. So edit the java source and replace the import statement at line #15

import javax.inject.Inject;

with

import jakarta.inject.Inject;

With that minor change in place you are now ready for the next step: build and deploy the plugin.

Step 4: Build and Deploy the Plugin

It’s as simple as running this on the command line:

> mvn clean package
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< example:ords-yaml-plugin >----------------------
[INFO] Building ords-yaml-plugin 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ ords-yaml-plugin ---
[INFO] Deleting /development/github/ords-yaml-plugin/target
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ ords-yaml-plugin ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /development/github/ords-yaml-plugin/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ ords-yaml-plugin ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /development/github/ords-yaml-plugin/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ ords-yaml-plugin ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /development/github/ords-yaml-plugin/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ ords-yaml-plugin ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /development/github/ords-yaml-plugin/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ ords-yaml-plugin ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running example.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.041 s - in example.AppTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ ords-yaml-plugin ---
[INFO] Building jar: /development/github/ords-yaml-plugin/target/ords-yaml-plugin-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.580 s
[INFO] Finished at: 2023-03-28T00:34:29+01:00
[INFO] ------------------------------------------------------------------------

Now your target/ords-yaml-plugin-1.0-SNAPSHOT.jar file really does have the capability of doing something interesting but it must be put in the lib/ext/ directory of your ORDS installation directory first. Many of the runtime dependencies will be met by the jars distributed with ORDS. However, this particular plugin requires certain YAML related jars to also be in the runtime classpath: snakeyaml and jackson-dataformat-yaml. These jars will also have to be copied to the lib/ext/ directory.

The jackson-dataformat-yaml artefact will be in your local Maven repository because it was pulled in for this ords-yaml-plugin project and that can just be copied over. However, for snakeyaml you may have to download that file.

Let’s assume the directory that ORDS was extracted to can be referred to by the environment variable $ORDS_HOME.

To download the snakeyaml jar so that it is included in the ORDS runtime classpath run this command:

> curl -o $ORDS_HOME/lib/ext/snakeyaml-1.33.jar \
https://repo1.maven.org/maven2/org/yaml/snakeyaml/1.33/snakeyaml-1.33.jar

To copy the jackson-dataformat-yaml jar so that it is included in the ORDS runtime classpath run this command:

> cp ~/.m2/repository/com/fasterxml/jackson/dataformat/jackson-dataformat-yaml/2.14.2/jackson-dataformat-yaml-2.14.2.jar \
   $ORDS_HOME/lib/ext/ 

Unless you want to change the versions of these jars used, you only need to do this once. Every time you build a new ords-yaml-plugin jar you will have to copy it to the ORDS library extension directory. To copy the jar file you built, run this command while in your ords-yaml-plugin project directory:

> cp target/ords-yaml-plugin-1.0-SNAPSHOT.jar \
   $ORDS_HOME/lib/ext/ 

Your ORDS directory should look like this:

ords-dist-22.4.4.041.1526
|-- FUTC.txt
|-- bin
|-- doc
|-- examples
|-- icons
|-- index.html
`-- lib
    |-- ext
    |   `-- README
    |   `-- jackson-dataformat-yaml-2.14.2.jar
    |   `-- ords-yaml-plugin-1.0-SNAPSHOT.jar
    |   `-- snakeyaml-1.33.jar
|-- license.txt
|-- linux-support
|-- ords.war
|-- scripts

Now just start ORDS in standalone mode with a configuration directory setup to use your preferred database.

> $ORDS_HOME/bin/ords --config /path/to/config serve
ORDS: Release 22.4 Production on Tue Mar 28 00:14:06 2023

Copyright (c) 2010, 2023, Oracle.

Configuration:
  /path/to/config/

2023-03-28T00:14:06.522Z INFO        HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8080
...
2023-03-28T00:14:23.618Z INFO        

Mapped local pools from /path/to/config/databases:
  /ords/                              => default                        => VALID     


2023-03-28T00:14:23.662Z INFO        Oracle REST Data Services initialized
Oracle REST Data Services version : 22.4.4.r0411526
Oracle REST Data Services server info: jetty/10.0.12
Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM 11.0.13+10-LTS-370

ORDS started up without issue so you plugin is effectively deployed and ready to work.

Step 5: Verify the Plugin

It’s time to confirm that the PluginYaml class does in fact transform the application/json response to text/yaml when the client requests that format. Send a request to ORDS that would normally return application/json but indicate that text/yaml is preferred. In my database I have the HR schema, and it’s EMPLOYEES table, REST Enabled and accessible at http://localhost:8080/ords/hr/employees/. The plugin is looking for Accepts header in the request to see if it should transform the response.

> curl -i -H "Accepts: text/yaml" 'http://localhost:8080/ords/hr/employees/?limit=2'
HTTP/1.1 200 OK
Content-Type: text/yaml
ETag: "PJjqS1hHizmvT8/WpBBub/3wIZ3HcdY6w/hNFD279DyLK4Prvk+q7BlnmHeKEfnQu7Ek7gGqeT86ltVBQcky6Q=="
Transfer-Encoding: chunked

---
items:
- employee_id: 100
  first_name: "Steven"
  last_name: "King"
  email: "SKING"
  phone_number: "515.123.4567"
  hire_date: "1987-06-17T00:00:00Z"
  job_id: "AD_PRES"
  salary: 24000
  commission_pct: null
  manager_id: null
  department_id: 90
  links:
  - rel: "self"
    href: "http://localhost:8080/ords/hr/employees/100"
- employee_id: 101
  first_name: "Neena"
  last_name: "Kochhar"
  email: "NKOCHHAR"
  phone_number: "515.123.4568"
  hire_date: "1989-09-21T00:00:00Z"
  job_id: "AD_VP"
  salary: 17000
  commission_pct: null
  manager_id: 100
  department_id: 90
  links:
  - rel: "self"
    href: "http://localhost:8080/ords/hr/employees/101"
hasMore: true
limit: 2
offset: 0
count: 2
links:
- rel: "self"
  href: "http://localhost:8080/ords/hr/employees/"
- rel: "edit"
  href: "http://localhost:8080/ords/hr/employees/"
- rel: "describedby"
  href: "http://localhost:8080/ords/hr/metadata-catalog/employees/"
- rel: "first"
  href: "http://localhost:8080/ords/hr/employees/?limit=2"
- rel: "next"
  href: "http://localhost:8080/ords/hr/employees/?offset=2&limit=2"

Conclusion

By following the steps in this article you have used Maven to compile and package an ORDS plugin. There were a few once of steps to get dependencies addressed but you now have a Maven project structure for developing more extensions to the existing ORDS behaviour.

If you’re looking to build an ORDS plugin, Apache Maven is a great choice because it provides a uniform build system and there is a huge range of Integrated Development Environments and Source Control Systems that work with Maven. Give it a try today and see how it can help you take your development efforts to the next level.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s