My previous example for adding a YAML filter to ORDS service responses no longer applies with ORDS 22.1.0 and later. In the past the approach for extending the ORDS functionality was to modify the distributed ords.war with a plugin jar. Now there is a separate lib/ext/ directory for extension jars. This post will revisit last year’s Plugin Example – Get YAML response for ORDS services and cover what is different now. The end goal will still be to have YAML returned for an AutoREST table rather than JSON.
ORDS 22.3.3 will be used. Available from https://oracle.com/rest
My client ‘Accepts’ YAML
As mentioned in the previous post, 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 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 22.3.3 with Oracle Java 11 in standalone mode. A configuration directory has already been created 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 22.3.3 ships with Jackson which will be the basis for the YAML conversation of the JSON response. However, other jars will be required at compile and runtime.
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
So far, all the steps have been the same as for last year’s example. Now, instead of adding the jars to the ords.war we add them to the existing lib/ext/ directory where ORDS has been installed.
- Copy the following files to ORDS home lib/ext/ directory:
- Start ORDS: ords –config /path/to/config/ serve
- 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/
Review of the artefacts
There are 3 jars to add to the lib/ext/ directory to be picked up in the ORDS classpath at runtime in. 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 lib/ext/ directory again.
Example output from running ant command:
Buildfile: /scratch/ords-184.108.40.2061.1929/examples/plugins/plugin-yaml/build.xml clean: [delete] Deleting directory /scratch/ords-220.127.116.111.1929/examples/plugins/plugin-yaml/built compile: [mkdir] Created dir: /scratch/ords-18.104.22.1681.1929/examples/plugins/plugin-yaml/built/classes [javac] Compiling 1 source file to /scratch/ords-22.214.171.1241.1929/examples/plugins/plugin-yaml/built/classes [javac] Note: Discovered type annotated with @Provides: example.PluginYaml dist: [jar] Building jar: /scratch/ords-126.96.36.1991.1929/examples/plugins/plugin-yaml/built/plugin-yaml.jar BUILD SUCCESSFUL Total time: 0 seconds
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 lib/ext/ are not supported by Oracle. The upshot is obvious. When it comes to plugins: test , test , test.
What about Tomcat and WebLogic?
The astute reader will have noticed that if the distributed ords.war is no longer modified then the additional jars will not be in the classpath of the ORDS web application when deployed to Apache Tomcat or Oracle WebLogic Server. That is where the ords war command comes in. Use the
ords war command to create a deployable web application archive file which has the
config.url context parameter explicitly set and any
jar files from
lib/ext folder are included.