Configuration Changes to Make after Installing ORDS Standalone

Jon DixonJon Dixon
9 min read

Introduction

These days, I nearly always deploy ORDS in Standalone mode. Standalone mode allows you to run ORDS without deploying it within a container like Tomcat or Weblogic, thus simplifying your technology stack. Oracle fully supports ORDS running in standalone mode. This is possible because ORDS comes bundled with its own HTTP server, Jetty.

Given that you are no longer deploying ORDS in a container, there are a few things you need to do to make sure you obtain comparable results. In this post, I will review the steps I typically take when configuring a new ORDS Server running in Standalone mode.

๐Ÿ”ข
This post was created based on ORDS version 23.2.3.r2421937

Prerequisites

Set Environment Variables

When I work with ORDS on the command line, I typically set the following environment variables for my user profile. These cut down on typing and provide a consistent way to access the ORDS binary and config files.

# Set a varibale to the Home Directory for ORDS. 
# You should see files like license.txt and index.html in this folder.
# This is also where the bin folder is located.
export ORDS_HOME=/opt/oracle/ords/
# Set a variable pointing to your ORDS Config files.
export ORDS_CONFIG=/etc/ords/config
# Add the ORDS binary to your Path.
export PATH=${ORDS_HOME}/bin:${PATH}

Enable Access Logs

Access logs are an essential part of securing your system and can also be helpful when troubleshooting issues.

You can enable access logs by running the following command:

ords --config $ORDS_CONFIG config set standalone.access.log /var/log/ords

ORDS: Release 23.2 Production on Mon Oct 02 22:28:36 2023
Copyright (c) 2010, 2023, Oracle.
Configuration:
  /var/log/ords
The global setting named: standalone.access.log was set to: /var/log/ords

This command will add the following entry to the $ORDS_CONFIG/global/settings.xml File: <entry key="standalone.access.log">/var/log/ords</entry> . After you restart ORDS, access logs will created in the /var/log/orda directory.

The resulting access log files will be named ords_YYYY_MM_DD.log and will be rotated daily. The log format matches the NCSA Common Log Format.

10.0.0.79 - - [02/Oct/2023:17:35:47 +0000] "GET http://10.0.0.74:8080/ords/demo/test/ping HTTP/1.1" 200 94
10.0.0.79 - - [02/Oct/2023:17:39:12 +0000] "POST /ords/wwv_flow.ajax?p_context=app-builder/page-designer/9123695687705 HTTP/1.1" 200 475
10.0.0.79 - - [02/Oct/2023:17:39:12 +0000] "POST /ords/wwv_flow.ajax?p_context=app-builder/page-designer/9123695687705 HTTP/1.1" 200 42503
10.0.0.79 - - [02/Oct/2023:17:39:12 +0000] "POST /ords/wwv_flow.ajax?p_context=app-builder/page-designer/9123695687705 HTTP/1.1" 200 1517
10.0.0.79 - - [02/Oct/2023:17:39:14 +0000] "POST /ords/wwv_flow.ajax?p_context=app-builder/page-designer/9123695687705 HTTP/1.1" 200 102
10.0.0.79 - - [02/Oct/2023:17:39:50 +0000] "POST /ords/wwv_flow.ajax?p_context=app-builder/page-designer/9123695687705 HTTP/1.1" 200 780
10.0.0.79 - - [02/Oct/2023:17:40:46 +0000] "GET http://10.0.0.74:8080/ords/demo/test/ping HTTP/1.1" 200 94
๐Ÿ—„
Don't forget to write a shell script to zip and archive these log files occasionally, as they can get quite large.

Enable Response Compression

As I mentioned in my post, Building Performant REST Services with Oracle REST Data Services compressing response content can bring significant performance benefits. You can enable response compression by creating a file called jetty-compression.xml in the folder $ORDS_CONFIG/global/standalone/etc.

Example Content for the file jetty-compression.xml

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
      <Call name="insertHandler">
        <Arg>
          <New id="GZipHanlder" class="org.eclipse.jetty.server.handler.gzip.GzipHandler">
            <Set name="IncludedMimeTypes">
              <Array type="java.lang.String">
                <Item>text/html</Item>
                <Item>text/xml</Item>
                <Item>text/css</Item>
                <Item>text/javascript</Item>
                <Item>application/javascript</Item>
                <Item>application/json</Item>
              </Array>
            </Set>
            <Set name="minGzipSize">128</Set>
          </New>
        </Arg>
      </Call>
</Configure>
  • IncludedMimeTypes is used to control what types of files should be zipped.

  • minGzipSize is used to control how many bytes the response must be before it is zipped.

Assign JVM Memory

I advise that you explicitly set the size of the JVM ORDS runs to take advantage of the available memory on the ORDS Server. For example, if an ORDS server has 4 GB of RAM, I typically assign 2 GB to the ORDS JVM.

You can set the JVM size by populating the environment variable _JAVA_OPTIONS in your ORDS startup script. The following example sets the JVM to 2 GB.

export _JAVA_OPTIONS="-Xms2048M -Xmx2048M"

ORDS Startup & Shutdown Scripts

You will need to create a startup and a shutdown script so you can set specific values before you start ORDS and so that you can run ORDS in the background.

I use an exact copy of Tim Hall's startup and shutdown scripts in this post. I am including them here for completeness. I have also changed the paths in these scripts to match those used in the rest of my post.

After creating the below scripts, you should make them executable as follows:

chmod u+x ~/scripts/*.sh

Startup Script

Create a file called ~/scripts/start_ords.sh with the following content (change paths as necessary):

#!/bin/bash
export PATH=/usr/sbin:/usr/local/bin:/usr/bin:/usr/local/sbin:$PATH
export JAVA_HOME=/u01/java/latest
export ORDS_HOME=/opt/oracle/ords/
export ORDS_CONFIG=/etc/ords/config
LOGFILE=/etc/ords/logs/ords-`date +"%Y""%m""%d"`.log
export _JAVA_OPTIONS="-Xms2048M -Xmx2048M"
nohup ${ORDS_HOME}/bin/ords --config ${ORDS_CONFIG} serve >> $LOGFILE 2>&1 &
echo "View log file with : tail -f $LOGFILE"

You can then start ORDS using the following command:

~/scripts/start_ords.sh

Shutdown Script

#!/bin/bash
export PATH=/usr/sbin:/usr/local/bin:/usr/bin:/usr/local/sbin:$PATH
kill `ps -ef | grep [o]rds.war | awk '{print $2}'`

You can then stop ORDS using the following command:

~/scripts/stop_ords.sh

Run ORDS as a Service

As an alternative to creating startup and shutdown scripts for ORDS, I often create a Linux Service to control ORDS startup and shutdown. The steps below were inspired by the blog post Run ORDS as a systemd Service by Christopher Ruepprich with changes to accommodate later versions of ORDS.

Steps

sudo vi /etc/systemd/system/ords.service
[Unit]
Description=ORDS Service

[Service]
SyslogIdentifier=ords
User=root
Restart=always
RestartSec=30
TimeoutStartSec=30
TimeoutStopSec=30
EnvironmentFile=/etc/ords/scripts/ords_exports.sh
ExecStart=/usr/bin/bash -c '${ORDS_HOME}/bin/ords --config ${ORDS_CONFIG} serve'
ExecStop=kill `ps -ef | grep [o]rds.war | awk '{print $2}'`

[Install]
WantedBy=multi-user.target

The file /etc/ords/scripts/ords_exports.sh looks like this:

PATH=/usr/sbin:/usr/local/bin:/usr/bin:/usr/local/sbin:$PATH
JAVA_HOME=/u01/java/latest
ORDS_HOME=/opt/oracle/ords
ORDS_CONFIG=/etc/ords/config
_JAVA_OPTIONS="-Xms1536M -Xmx1536M"

You can then start/stop ORDS with these commands:

sudo systemctl start ords
sudo systemctl stop ords
sudo systemctl restart ords

Finally, you can view the ORDS Log files as follows:

journalctl -f -u ords.service

Running ORDS Behind a Load Balancer

Usually, when I deploy ORDS, I deploy it behind a Load Balancer. The Load Balancer handles receiving HTTPS requests over SSL/TLS and routes requests to one or more ORDS servers (preferably more) running over HTTP. In this scenario, SSL Termination is happening in ORDS.

โ„น
Of course, you can also add the same certificate to the ORDS server that you add to your Load Balancer and have HTTPS on your ORDS Server and the Load Balancer. See this post for more details on adding an SSL Certificate to your ORDS Server.

If you do terminate SSL at the Load Balancer, then there are two alternate configurations that you need to make. Configuration 1 works fine with an OCI Load Balancer but not other Load Balancers (e.g., HA Proxy). If Configuration 1 does not work for you, then try Configuration 2.

Configuration 1

# Allow access from example.com
ords --config $ORDS_CONFIG config set security.externalSessionTrustedOrigins https://example.com
# Verify HTTP Header Contains X-Forwarded-Proto: https
ords --config $ORDS_CONFIG config set security.httpsHeaderCheck "X-Forwarded-Proto: https"
<entry key="security.externalSessionTrustedOrigins">https://example.com</entry>
<entry key="security.httpsHeaderCheck">X-Forwarded-Proto: https</entry>

Configuration 2

# Allow access from example.com
ords --config $ORDS_CONFIG config set security.externalSessionTrustedOrigins https://example.com
# Force HTTPS
ords --config $ORDS_CONFIG config set security.forceHTTPS true

This will result in two new settings being added to $ORDS_CONFIG/global/settings.xml.

<entry key="security.externalSessionTrustedOrigins">https://example.com</entry>
<entry key="security.forceHTTPS">true</entry>

Other Items

While not specific to running ORDS in Standalone mode, you should consider the following when firing up a new ORDS server.

Size the ORDS Connection Pool

ORDS uses a database connection pool to connect to the Oracle Database. You must size this correctly when configuring a new ORDS instance. I won't go into how to size the database pool in this post, but you should set the below settings based on your determination of the pool size:

  • jdbc.InitialLimit - Determines how many DB connections ORDS creates when you first start ORDS.

  • jdbc.MinLimit - Determines how many DB connections ORDS should keep open even when no activity exists.

  • jdbc.MaxLimit - Determines the maximum number of DB connections ORDS is allowed to create.

ORDS Connection Diagram

๐Ÿ’ก
The challenge is balancing how many connections you want ORDS to use during the quiet times versus how long it takes for ORDS to start new connections when demand increases.

The following ORDS config commands set these values in the $ORDS_CONFIG/global/settings.xml file.

ords --config $ORDS_CONFIG config set jdbc.InitialLimit 15
ords --config $ORDS_CONFIG config set jdbc.MinLimit 15
ords --config $ORDS_CONFIG config set jdbc.MaxLimit 25

Entries in $ORDS_CONFIG/global/settings.xml

<entry key="jdbc.InitialLimit">15</entry>
<entry key="jdbc.MaxLimit">25</entry>
<entry key="jdbc.MinLimit">15</entry>

Initial = Min = Max

Thanks to Connor McDonald for his comment on this post. In his comment, he refers to this very detailed presentation from Toon Koppelaars on connection pools and introduces an interesting approach to connection pool sizing. His comment in full (with some edits from Grammarly) is this:

The TL;DR rationale from the presentation for that recommendation is:

  • unused pool sessions are idle; they do nothing and consume minimal resources because the last thing pool-based apps do is free their resources when they give back a connection. If they don't, you have an app bug, not a connection pool config issue.

  • the max size setting (by definition) is what you are prepared to let your server handle without undue stress, so why would you pick a minimum/initial less than that? To do so means your apps incur the cost of creating a connection pseudo-randomly. That makes response times variable.

๐Ÿ’ก
I thought Connor's point was well worth adding to the post. Maybe because my dad was an Accountant, the thought of the wasted CPU prevented me from thinking of this before ๐Ÿ˜Š

Enable Metadata Caching

When a client calls an ORDS REST Service, ORDS must first fetch the REST service definition from the ORDS Metadata and then execute the code contained in the REST service definition.

With Metadata Caching turned on, ORDS can get the REST Service definition from a cached version on the ORDS server, eliminating a round trip to the database.

ORDS Metadata Caching

You can enable Metadata Caching by adding the following properties to the $CONFIG_HOME/global/settings.xml file.

ords --config $ORDS_CONFIG config set cache.metadata.enabled true
ords --config $ORDS_CONFIG config set cache.metadata.timeout 1m
PropertyDescription
cache.metadata.enabledEnable or disable metadata caching.
cache.metadata.timeoutSpecify how long the metadata remains in the cache before the cache is refreshed from ORDS Metadata in the database.

Conclusion

Running ORDS in Standalone mode is fully supported by Oracle and allows you to remove Tomcat or Weblogic Containers from your ORDS stack. Following the suggestions in this post will enable you to achieve this while maintaining the features and performance these containers provided in the past.

Special Thanks

  • Thanks to Richard Soule for his comment regarding placing the log files on a separate disk partition. This way, if the logs fill the partition, ORDS will keep running.

  • Thanks to Jeff Smith for his comment on LinkedIn: "As for default memory allocation for the JVM, it depends on version of Java but basically comes down to the % of the physical memory on the device/machine."

  • Thanks to the suggestion from Alain LACOUR on LinkedIn: "Beyond creating start and stop scripts I would even recommend to run ORDS standalone as a service so it can start by itself and stop properly when the server is restarted for any reason."

๐Ÿ”— Read More

16
Subscribe to my newsletter

Read articles from Jon Dixon directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jon Dixon
Jon Dixon

Hi, thanks for stopping by! I am focused on designing and building innovative solutions using the Oracle Database, Oracle APEX, and Oracle REST Data Services (ORDS). I hope you enjoy my blog.