Ilmar Kerm

Oracle, databases, Linux and maybe more

I’m forced to write my first JavaScript related post 🙁 Oh, well.

ORDS version at the time of writing is 24.3.

Oracle Rest Data Services (ORDS) does support (currently read-only) GraphQL protocol for serving data from Oracle rest enabled tables. You can read more about it here: https://oracle-base.com/articles/misc/oracle-rest-data-services-ords-graphql

To get GraphQL support working, ORDS need to be running using GraalVM JDK – but it’s not as simple as switching the JDK – GraalVM also needs to support JavaScript polyglot engine. When I started looking into this world I was properly confused, starting with naming – ORDS 24.3 installation checklist still requires GraalVM Enterprise Edition and when going to search for it find man-bear-pigs like GraalVM-EE-23-for-JDK-17. Properly confusing for a non-developer like me.

Luckily naming has been significantly simplified recently and GraalVM EE is dead

Naming starting from GraalVM for JDK21 is simplified, but what has gone much more complicated is installing JavaScript polyglot libraries for GraalVM. With GraalVM for JDK 17 there was a command “gu install” for it, but it has been removed starting from GraalVM for JDK 21.

ORDS installation checklist 24.3 acknowledges it, but then gives some strange XML code on how to install them. Not helpful for non-developers, like me. This XML is intended to describe dependencies for Java project (using Maven), so during build the dependencies would be fetched automatically. But I have nothing to build – GraphQL support is already in ORDS, I just need the dependencies downloaded.

I think, if the extra libraries are needed for ORDS built in functionality to work, ORDS should include them by default.

Installation steps

I know pretty much all software can be downloaded using Oracle provided yum repositories, but here I’m doing everything manually, to be able to control the versions precisely. And not to mess with RPM-s, unzipping this is so much easier and predictable and more usable across all possible Linux distributions.

All software is placed under /home/ords in my example.

First lets download GraalVM for JDK 21 itself. Oracle has started to offer non-website-clicking “script friendly” URLs that always point to the latest version, you can get them here. I’m not going to use ANY latest URL Oracle offers on purpose, since I’m an automation guy I need to be able to download predictable and internally tested versions of the software and be able to validate the downloaded software against known checksum value.

Download the software, all versions are current at the time of writing, but of course are very soon out of date

# GraalVM for JDK21
https://download.oracle.com/graalvm/21/archive/graalvm-jdk-21.0.5_linux-x64_bin.tar.gz

# Maven
https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz

# ORDS
https://download.oracle.com/otn_software/java/ords/ords-24.3.0.262.0924.zip

In Ansible something like this, also unzipping them. Take it as an example, and not copy it blindly

# Facts
graalvm_download_url: "https://download.oracle.com/graalvm/21/archive/graalvm-jdk-21.0.5_linux-x64_bin.tar.gz"
graalvm_download_checksum: "sha256:c1960d4f9d278458bde1cd15115ac2f0b3240cb427d51cfeceb79dab91a7f5c9"
graalvm_install_dir: "{{ ords_install_base }}/graalvm"

maven_download_url: "https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz"
maven_download_checksum: "sha512:a555254d6b53d267965a3404ecb14e53c3827c09c3b94b5678835887ab404556bfaf78dcfe03ba76fa2508649dca8531c74bca4d5846513522404d48e8c4ac8b"
maven_install_dir: "{{ ords_install_base }}/maven"

ords_download_url: "https://download.oracle.com/otn_software/java/ords/ords-24.3.0.262.0924.zip"
ords_installer_checksum: "sha1:6e8d9b15faa232911fcff367c99ba696389ceddc"
ords_install_dir: "{{ ords_install_base }}/ords"
ords_install_base: "/home/ords"

# Tasks
- name: Download GraalVM
  ansible.builtin.get_url:
      url: "{{ graalvm_download_url }}"
      dest: "{{ ords_install_base }}/graalvm.tar.gz"
      checksum: "{{ graalvm_download_checksum }}"
      use_proxy: "{{ 'yes' if http_proxy is defined and http_proxy else 'no' }}"
  environment:
      https_proxy: "{{ http_proxy }}"
  register: graalvm_downloaded
- name: Unzip GraalVM
  ansible.builtin.unarchive:
      remote_src: yes
      src: "{{ ords_install_base }}/graalvm.tar.gz"
      dest: "{{ graalvm_install_dir }}"
      extra_opts:
          - "--strip-components=1"
  when: graalvm_downloaded is changed
# Download maven
- name: Download Maven
  ansible.builtin.get_url:
      url: "{{ maven_download_url }}"
      dest: "{{ ords_install_base }}/maven.tar.gz"
      checksum: "{{ maven_download_checksum }}"
      use_proxy: "{{ 'yes' if http_proxy is defined and http_proxy else 'no' }}"
  environment:
      https_proxy: "{{ http_proxy }}"
  register: maven_downloaded
- name: Unzip maven
  ansible.builtin.unarchive:
      remote_src: yes
      src: "{{ ords_install_base }}/maven.tar.gz"
      dest: "{{ maven_install_dir }}"
      extra_opts:
          - "--strip-components=1"
  when: maven_downloaded is changed
# Download ORDS
- name: Download ORDS
  ansible.builtin.get_url:
      url: "{{ ords_download_url }}"
      dest: "{{ ords_install_base }}/ords-latest.zip"
      checksum: "{{ ords_installer_checksum }}"
      use_proxy: "{{ 'yes' if http_proxy is defined and http_proxy else 'no' }}"
  environment:
      https_proxy: "{{ http_proxy }}"
  register: ords_downloaded
- name: Unzip ORDS installer
  ansible.builtin.unarchive:
      remote_src: yes
      src: "{{ ords_install_base }}/ords-latest.zip"
      dest: "{{ ords_install_dir }}"
  when: ords_downloaded is changed

Now the complicated part, adding GraalVM JavaScript polyglot libraries

Create pom.xml file in some directory with contents. 24.1.1 is the current JavaScript engine version, you can see the available versions in https://mvnrepository.com/artifact/org.graalvm.polyglot/polyglot

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>eu.ilmarkerm.ords</groupId>
  <artifactId>ords-graaljs-download</artifactId>
  <version>1.1.1</version>
  <url>https://ilmarkerm.eu</url>
  <name>POM to download GraalVM JavaScript engine</name>
  <dependencies>
    <dependency>
        <groupId>org.graalvm.polyglot</groupId>
        <artifactId>polyglot</artifactId>
        <version>24.1.1</version>
    </dependency>
    <dependency>
        <groupId>org.graalvm.polyglot</groupId>
        <!-- Language: js -->
        <artifactId>js</artifactId>
        <version>24.1.1</version>
        <type>pom</type>
    </dependency>
  </dependencies>
</project>

Execute maven to download the required libraries and place them under ORDS libraries

/home/ords/maven/bin/mvn dependency:copy-dependencies -DoutputDirectory=/home/ords/ords/lib/ext -DuseBaseVersion=true

It will download files like these (polyglot 24.1.1):

collections-24.1.1.jar
icu4j-24.1.1.jar
jniutils-24.1.1.jar
js-language-24.1.1.jar
nativebridge-24.1.1.jar
nativeimage-24.1.1.jar
polyglot-24.1.1.jar
regex-24.1.1.jar
truffle-api-24.1.1.jar
truffle-compiler-24.1.1.jar
truffle-enterprise-24.1.1.jar
truffle-runtime-24.1.1.jar
word-24.1.1.jar

Start ORDS (using GraalVM JDK) and then ORDS support for GraphQL is ready to be used.

Conclusion

I’m not a java developer, so things might be wrong here 🙂

But I do hope the situation improves over the next couple of ORDS versions.

Oracle Database has had the possibility to run Java code inside the database for a long time. It’s a very rare occasion when you need to use it but still. Here is one example I used to download content from HTTPS website that required user certificates for authentication. Please take the code below more as an example how to put simple Java code inside the database, not as a solution for user certificates authentication, because UTL_HTTP can do the same thing (although I wasn’t successful in implementing it under 11.2.0.2).

First, load the Java source into database. The code below shows:

  • How to return simple datatype (int) from Java function – makeConnection
  • How to return Oracle CLOB datatype from Java – makeConnectionClob
  • How to execute SQL from Java, in the same calling session

Note that method main is just added for testing from command line.

Then you need to create a wrapper package in database. This declares the PL/SQL wrapper function names and input/output parameters.

Download the source: java_source.java and PL/SQL wrapper.sql.

When you first execute the code, you will most likely get some privilege errors, but the error message will tell you how to grant the needed privileges. For example, for this code the following grants were needed:

exec dbms_java.grant_permission( 'OWNER', 'SYS:java.util.PropertyPermission', 'javax.net.ssl.keyStore', 'write' );
exec dbms_java.grant_permission( 'OWNER', 'SYS:java.util.PropertyPermission', 'javax.net.ssl.trustStore', 'write' );
exec dbms_java.grant_permission( 'OWNER', 'SYS:java.util.PropertyPermission', 'javax.net.ssl.keyStorePassword', 'write' );
exec dbms_java.grant_permission( 'OWNER', 'SYS:java.net.SocketPermission', 'site.that.requires.user.cert', 'resolve' );
exec dbms_java.grant_permission( 'OWNER', 'SYS:java.net.SocketPermission', '1.2.3.4:443', 'connect,resolve' );