RESTful services often involve JSON structures for request and response payloads. On occasion there is a requirement to integrate with another service or system that does not use JSON or expects a particular JSON structure. This can be catered for in an ORDS plsql/block based service and in practice may be the most performant approach to take for individual services. Imagine a scenario where existing services, perhaps from AutoREST enabling tables and views, you want a YAML representation option as well. ORDS provides a plugin framework which makes it possible to extend the functionality and introduce custom behaviour such as this.

Default content type: application/json
When an ORDS service is created, by rest enabling a table for example, the default content type for requests and response payloads is application/json. The following request to get data from the EMPLOYEES table in the HR schema…
curl -i http://localhost:8080/ords/hr/employees/
…will return this response…
HTTP/1.1 200 OK Content-Type: application/json ETag: "v7RO6F9A6fwTqkJvp28hXrluD1r31Uo4stahc5jCzCPtXKk8ke8S0cEcmED1FqOT2PdZ/VkEqgREVjTp2sWptA==" Transfer-Encoding: chunked { "items" : [ { "commission_pct" : null, "department_id" : 90, "email" : "SKING", "employee_id" : 100, "first_name" : "Steven", "hire_date" : "1987-06-16T23:00:00Z", "job_id" : "AD_PRES", "last_name" : "King", "links" : [ { "href" : "http://localhost:8080/ords/hr/employees/100", "rel" : "self" } ], "manager_id" : null, "phone_number" : "515.123.4567", "salary" : 24000 }, ...removed for brevity... ], "count" : 25, "hasMore" : true, "limit" : 25, "links" : [ { "href" : "http://localhost:8080/ords/hr/employees/", "rel" : "self" }, ...removed for brevity... ], "offset" : 0 }
Accepting YAML
The end goal for this exercise is to convert any application/json response to a text/yaml response without having to modify the service implementation. The client specifies that YAML should be returned by stating that text/yaml is acceptable…
curl -i -H "Accepts: text/yaml" http://localhost:8080/ords/hr/employees/
…which gets this response
HTTP/1.1 200 OK Content-Type: text/yaml ETag: "v7RO6F9A6fwTqkJvp28hXrluD1r31Uo4stahc5jCzCPtXKk8ke8S0cEcmED1FqOT2PdZ/VkEqgREVjTp2sWptA==" Transfer-Encoding: chunked --- items: - employee_id: 100 first_name: "Steven" last_name: "King" email: "SKING" phone_number: "515.123.4567" hire_date: "1987-06-16T23: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" ...removed for brevity... hasMore: true limit: 25offset: 0 count: 25 links: - rel: "self" href: "http://localhost:8080/ords/hr/employees/" ...removed for brevity...
ORDS Custom Plugin
To achieve this we will code our own custom plugin to ORDS. The ORDS distribution from oracle.com/rest contains example plugins and for simplicity one of these examples will be copied as the basis of our new custom plugin. First I’ll provide the code and steps to build the plugin. Then I will go through the plugin code to explain key parts.
Assumptions and prerequisites
This example is based on the plugin examples in the ORDS Getting Started tutorial. It is assumed that you are already familiar with that plugin-demo Servlet example.
We’re using ORDS 21.4.0 with Oracle Java 1.8 in standalone mode. It has already been setup with a configdir containing valid connection details to a database that has ORDS installed. There is a REST Enabled schema in the database with a REST Enabled table that does not require authentication to access. In my example the schema is HR, the table is EMPLOYEES and the RESTful endpoint is http://localhost:8080/ords/hr/employees.
ORDS 21.4.0 ships with Jackson 2.13.0 so any other jars that are required at compile or runtime will be related to that particular release. If you are using a later version of ORDS you should use the Jackson release that is applicable.
In this article I will refer to the new plugin as plugin-yaml but you can use any name that suits.
- Copy the examples/plugins/plugins-demo directory to examples/plugins/plugin-yaml
- Change the project name in the build.xml to plugin-yaml
- <project default=”dist” name=”plugin-yaml”>
- Remove the examples/plugins/plugin-yaml/src/example/PluginDemo.java
- Download PluginYaml.java to examples/plugins/plugin-yaml/src/example/
- Download jackson-core-2.13.0.jar to examples/plugins/plugin-yaml/lib/
- Download jackson-databind-2.13.0.jar to examples/plugins/plugin-yaml/lib/
- Download jackson-dataformat-yaml-2.13.0.jar to examples/plugins/plugin-yaml/lib/
- Download snakeyaml-1.28.jar to examples/plugins/plugin-yaml/lib/
- Open a command shell at the examples/plugins/plugin-yaml/ directory
- At the command line run ant
- At the command line run java -jar ../../../ords.war plugin built/plugin-yaml.jar
- At the command line run java -jar ../../../ords.war plugin lib/jackson-dataformat-yaml-2.13.0.jar
- At the command line run java -jar ../../../ords.war plugin lib/snakeyaml-1.28.jar
- Start ORDS: java -jar ../../../ords.war standalone
- Send a request for JSON data: curl -i http://localhost:8080/ords/hr/employees/
- Send a request for YAML data: curl -i -H “Accepts: text/yaml” http://localhost:8080/ords/hr/employees/
There are 3 jars to add to the ords.war as plugins. The plugin-yaml.jar which we have built from source and the 2 runtime dependencies jackson-dataform-yaml and snakeyaml. Any subsequent code changes in examples/plugins/plugin-yaml/src/ will require the ant project to be built again but only the produced plugin-yaml.jar must be added to the ords.war again.
Example output from running ant command:
Buildfile: /scratch/ords-21.4.0.348.1956/examples/plugins/plugin-yaml/build.xml clean: [delete] Deleting directory /scratch/ords-21.4.0.348.1956/examples/plugins/plugin-yaml/built compile: [mkdir] Created dir: /scratch/ords-21.4.0.348.1956/examples/plugins/plugin-yaml/built/classes [javac] Compiling 1 source file to /scratch/ords-21.4.0.348.1956/examples/plugins/plugin-yaml/built/classes [javac] Note: Discovered type annotated with @Provides: example.PluginYaml dist: [jar] Building jar: /scratch/ords-21.4.0.348.1956/examples/plugins/plugin-yaml/built/plugin-yaml.jar BUILD SUCCESSFUL Total time: 0 seconds
Code Overview
The PluginYaml.java is a basic javax.servlet.Filter implementation that replaces the response output stream when the servlet container is about to return that response to the calling client. To achieve this, when it is determined that the client provides Accepts: text/yaml in the request, the filter supplies a HttpServletResponseWrapper which captures the original response. That wrapper, in this case called ServletResponseWrapperCopier, uses Jackson mappers to produce a YAML representation of the JSON response content and return that instead.
In summary, the filter…
- Decides if capture and replace might be required: https://github.com/pobalopalous/pobalopalous/blob/main/example/PluginYaml.java#L62
- Captures the output stream from the service implementation: https://github.com/pobalopalous/pobalopalous/blob/main/example/PluginYaml.java#L94
- Replaces the output stream with a YAML representation: https://github.com/pobalopalous/pobalopalous/blob/main/example/PluginYaml.java#L143
Terms and Conditions
The performance overhead of producing a response in one structure and creating a copy of it in a different structure may be quite significant for large payloads. Although ORDS provides a plugin framework for you to add functionality, your custom plugin, or any third party jars that you add to the ords.war are not supported by Oracle. The upshot is obvious. When it comes to plugins: test , test , test.
One thought on “Plugin Example – Get YAML response for ORDS services”