How to check if ORDS is running

Oracle REST Data Services (ORDS) provides web services for an Oracle Database and allows for access to the database over the web. In a recent YouTube video I showed how to use ORDS in the new Oracle Developer DB Virtual Machine and briefly mentioned how to find the running ORDS process. Let’s go into that in more detail. In this article, we will discuss how to check if ORDS is running in a standalone ORDS deployments as well as deployments on Apache Tomcat and Oracle WebLogic Server.

  1. Checking ORDS on Standalone Deployments
  2. Checking ORDS on Apache Tomcat
  3. Checking ORDS on Oracle WebLogic Server
  4. What about Windows systems?
  5. Conclusion

Checking ORDS on Standalone Deployments

On a unix based system, the simplest way to check if ORDS is running is to use the jps command. This command will list all Java process that are currently running on the system. It is important to note that in order for jps to work, the JDK must be installed and the PATH environment variable must be set correctly. The jps command accepts a number of options and the most useful in this case are

  • -m to display the arguments passed to the main method
  • -l to display the full path name to the ords.war location. Pass that through a grep for ords to find the ords specific java process.
  • -v displays the arguments passed to the JVM.
jps -mlv | grep ords
8545 /home/oracle/ords/ords.war --config /home/oracle/ords_config serve --secure --port 443 -Doracle.dbtools.cmdline.home=/home/oracle/ords -Duser.language=en -Duser.region=US -Dfile.encoding=UTF-8 -Djava.awt.headless=true -Dnashorn.args=--no-deprecation-warning -Doracle.dbtools.cmdline.ShellCommand=ords -Duser.timezone=UTC

If ORDS is running in standalone mode, you should see a process with ords.war in the path. If this process is listed, then ORDS is running. Note that due to the -v option you also see the Java option oracle.dbtools.cmdline.home used. That should be the folder that the ords.war is found in and should also be the distribution directory. In the above example, the distribution directory is /home/oracle/ords/ and the ORDS command used to start ORDS was actually /home/oracle/ords/bin/ords --config /home/oracle/ords_config serve --secure --port 443

Another approach is to use the ps command. This command will list all processes currently running on the system. To check if ORDS is running, use the command ps -ef | grep ords.war. If you see a process with ords.war as the command, then ORDS is running.

[oracle@localhost ~]$ ps -ef | grep ords.war
oracle      8545    8516  2 21:13 pts/1    00:00:49 /home/oracle/java/jdk-11.0.17/bin/java -Doracle.dbtools.cmdline.home=/home/oracle/ords -Duser.language=en -Duser.region=US -Dfile.encoding=UTF-8 -Djava.awt.headless=true -Dnashorn.args=--no-deprecation-warning -Doracle.dbtools.cmdline.ShellCommand=ords -Duser.timezone=UTC -jar /home/oracle/ords/ords.war --config /home/oracle/ords_config serve --secure --port 443
oracle      9554    3521  0 21:42 pts/0    00:00:00 grep --color=auto ords.war

You’ll also see similar information as with the jps command but also the java executable used at start up too. Note that access to run ps is not always guaranteed and it main not be available for the user you are logged in as.

One note about the command line arguments one can see for the running Java processes. It can be seen by other users. This is one of the reasons that ORDS does not accept a password as a command line option. That password would be visible to anyone able to list the running processes. Either through jps or ps.

Checking ORDS on Apache Tomcat

If it’s enabled, use the Tomcat manager web application, or at the very least the catalina log files, to confirm that ORDS web application is deployed and started. If not, then jps on the command line is the next logical choice.

When ORDS is deployed on Apache Tomcat, you can use the jps command to check if the server is running and get further information about where the ords.war might be deployed. Run jps -mlv as before but this time grep for the catalina package.

jps -mlv | grep catalina
88 org.apache.catalina.startup.Bootstrap start --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Dconfig.url=/u01/oracle/properties/config -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp

The above indicates that catalina.base is /usr/local/tomcat and most people use the automatic web application deployment by placing the ords.war in the $CATALINA_BASE/webapps/ directory.

ls -la /usr/local/tomcat/webapps/
total 92384
drwxr-xr-x 1 root root       62 Apr 11 06:40 .
drwxr-xr-x 1 root root       77 Apr 11 06:40 ..
drwxr-x--- 5 root root       67 Apr 11 05:38 ords
-rw-r--r-- 1 root root 94599760 Apr 10 19:45 ords.war
drwxr-xr-x 3 root root       45 Apr 11 05:38 ROOT

That directory listing shows the ords.war file and that it has been exploded by Tomcat into the ords directory. It’s not a guarantee that ORDS was deployed successfully, one would have to check the Tomcat logs, but it is a good indicator.

Checking ORDS on Oracle WebLogic Server

If it’s enabled, use the WebLogic console or WLST to confirm that ORDS web application is deployed and started.

Similar to Apache Tomcat, the Oracle WebLogic Server is a Java application so yet again, it is the jps utility that is the starting point to get information on the server(s) running and their configuration.

jps -mvl | grep weblogic
80 weblogic.Server -Djava.security.egd=file:/dev/./urandom -Dlaunch.use.env.classpath=true -Dweblogic.Name=AdminServer -Djava.security.policy=/u01/oracle/wlserver/server/lib/weblogic.policy -Dconfig.url=/u01/oracle/properties/config -Djava.system.class.loader=com.oracle.classloader.weblogic.LaunchClassLoader -javaagent:/u01/oracle/wlserver/server/lib/debugpatch-agent.jar -da -Dwls.home=/u01/oracle/wlserver/server -Dweblogic.home=/u01/oracle/wlserver/server

The output indicates that the weblogic.home is /u01/oracle/wlserver/server. Unlike with Tomcat, one is not going to find a straight forward web application deployment directory under the weblogic.home. There’s a little more digging required to check if a server is configured to deploy ORDS and we’ll use the weblogic.Name parameter ( AdminServer in this case) to when digging deeper. The weblogic.home just indicates where the server java application is being executed from. The server runtime configuration is being picked up separately. In the majority of installations that will be in a related user_projects/domains directory found two levels up. For the above WLS instance that would be /u01/oracle/user_projects/domains/.

That directory may have the configuration for multiple domains but quite often there is just one: base_domain. From there you can get a little more information on how the server identified by the above weblogic.Name. The domain configuration is persisted in /u01/oracle/user_projects/domains/base_domain/config/config.xml and you can see there is an app-deployment configuration targetting the AdminServer.

<?xml version="1.0" encoding="UTF-8"?>
<domain xsi:schemaLocation="http://xmlns.oracle.com/weblogic/security/wls http://xmlns.oracle.com/weblogic/security/wls/1.0/wls.xsd http://xmlns.oracle.com/weblogic/domain http://xmlns.oracle.com/weblogic/1.0/domain.xsd http://xmlns.oracle.com/weblogic/security http://xmlns.oracle.com/weblogic/1.0/security.xsd http://xmlns.oracle.com/weblogic/security/xacml http://xmlns.oracle.com/weblogic/security/xacml/1.0/xacml.xsd" xmlns="http://xmlns.oracle.com/weblogic/domain" xmlns:sec="http://xmlns.oracle.com/weblogic/security" xmlns:wls="http://xmlns.oracle.com/weblogic/security/wls" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <name>base_domain</name>
  <domain-version>14.1.1.0.0</domain-version>
...
  <server>
    <name>AdminServer</name>
    <ssl>
      <name>AdminServer</name>
      <enabled>true</enabled>
    </ssl>
    <listen-address/>
  </server>
...
  <app-deployment>
    <name>ords</name>
    <target>AdminServer</target>
    <source-path>/u01/oracle/ords/ords.war</source-path>
    <staging-mode>nostage</staging-mode>
  </app-deployment>
...
</domain>

If you see this app-deployment configuration in place it is not a complete guarantee that ORDS successfully deployed when the server started but it is a good indication.

What about Windows systems?

The syntax for path separates might be different but the same jps utility options apply and the relative file locations will also be the same for standalone, Tomcat and WebLogic.

Conclusion

In summary, the jps utility, also known as the Java Virtual Machine Process Status Tool, that comes with your Java SE distribution is your friend. Just from the command line, it can guide you in discovering more about the ORDS instance running on your machine irrespective of deployment mode.

ORDS Standalone vs Tomcat vs WebLogic

When it comes to deploying Oracle REST Data Services (ORDS), there are three main options to consider: Standalone, Apache Tomcat, and Oracle WebLogic Server. Each has its own advantages and drawbacks, so it’s important to understand the differences between them before choosing a deployment option.

ORDS Standalone

ORDS Standalone is the simplest deployment option and requires no external application servers. In fact, once you have ORDS, you have all you need to get started. It uses the Eclipse Jetty server embedded in ORDS, which is suitable for development, testing and production environments. It’s easy to set up and manage. It’s simplicity makes it ideal for smaller workloads but can also scale for high availability and greater throughput when a load balancer is put in front multiple ORDS standalone instances.

To run ORDS in standalone mode: ords --config /path/to/config/ serve

One should note that although using ORDS with Apache Tomcat or Oracle WebLogic Server is supported, quite often the diagnosis process for any support issues will involve verifying if the issue also occurs with ORDS standalone.

As mentioned in a previous article about Application Process Monitoring, when running in standalone mode, ORDS loads the jars from the ords.war into memory and some Java Agents which modify jars to instrument classes at the byte level can interfere with that classloading process. Examples of Java Agents which can not be used when ORDS standalone mode is used: Oracle APM Java Agent, DynaTrace Java Monitoring.

Pros:

  • Easy to set up and manage. Get started straight away!
  • Can generate a convenient self-signed certificate for HTTPS.
  • Suitable for development and testing.
  • Ideal for a variety of workloads.
  • ORDS configuration of embedded Jetty server optimised for REST services.
  • Jetty configuration is extensible using XML files.

Cons:

  • Limited integration with identity and authorisation management systems.
  • Requires a load balancer for high availability.
  • Does not work with some Java Agents.

ORDS Deployment to Tomcat

Apache Tomcat powers numerous large-scale, mission-critical web applications across a diverse range of industries and organisations. Chances are that your organisation is already running at least one Apache Tomcat servlet container. Tomcat is a popular open-source web server that is well-suited to ORDS.

It’s easy to set up, and deploying ORDS to Apache Tomcat is as simple as ords --config /path/to/config/ war $CATALINA_HOME/webapps/ords.war

Of course, that’s if your Tomcat configuration has auto deployment enabled, which is the default setting. Similar to ORDS standalone, Tomcat is configured to be reasonably secure for most use cases by default. Also, similar to ORDS standalone, for high availability a load balancer / reverse proxy must be configured to route to the servers.

Pros:

  • Integration with identity and authorisation management systems, such as Active Directory, OpenID Connect, through container managed security.
  • Suitable for production workloads.
  • Easy to get started with: install Apache Tomcat, start it, generate the ords web application, done.
  • Free.

Cons:

  • Involves an additional server.
  • More complex to manage than ORDS Standalone for clustering – see Tomcat Cluster documentation. Moreover, since ORDS is stateless, session serialisation, which is a common characteristic of web server clustering, that aspect of most clustered systems is not required.

ORDS Deployment to WebLogic

Oracle WebLogic Server is a unified and extensible platform for developing, deploying and running enterprise applications, such as Java, for on-premises and in the cloud. ORDS deployment to WebLogic is a very robust option. Weblogic is a powerful and reliable application server and provides advanced features such as clustering and load balancing. It’s suitable for large-scale production workloads. However, provisioning and configuring an Oracle Weblogic domain can be complicated.

The steps for deploying ORDS to WebLogic are involved and it’s best to refer to ORDS documentation.

Pros:

  • Provides advanced features such as clustering and load balancing
  • Suitable for large-scale production workloads
  • Robust and reliable
  • Integration with identity and authorisation management systems, such as Active Directory, OpenID Connect, through container managed security.

Cons:

  • Requires an Oracle WebLogic application server licence for production
  • Complex to set up and manage but is fairly standard for an enterprise grade application server

Decision Factors

Ultimately, the deployment option that’s best for you depends on the complexity of your integration with other systems. What your organisation already uses, and has support in place for, is also an important factor. ORDS standalone is ideal for getting started, developing and testing new services before deployment to product. It’s also suitable for production workloads. What it misses out of the box is integration with identity and authorisation management systems. That’s essentially the gap that deployment on Tomcat and Weblogic addresses. In all three cases, each mode is suitable for large-scale production workloads with appropriate load balancing in place.


Post Publishing Edits:

February 20th 2023 - Added text about some Java Agents not working with ORDS standalone.
February 25th 2023 - Added text pointing out that ORDS does not retain session state.

Easily deploy Oracle REST Data Services on Apache Tomcat 10.1


Free and open source software (FOSS) has become an integral part of our lives. For those of us developing, hosting or supporting software applications anyway. Whether it’s a web server, a database, or a programming language, open source software is everywhere. That could be a single jar or javascript library, a framework, a utility or an entire software stack. It is hard to avoid it. Sometimes the free and open source option is the reference implementation for a specification which has a commercial alternative but it can be difficult to justify the cost of paying for that software. More often then not, one can find that modern commercial software systems are built with, or rely on, some form of free and open source software. It’s no surprise, considering all the benefits that come with it. Here are just a few reasons why it makes sense to use FOSS.

The case for free and open source

The reasons for choosing free and open source are compelling.

First and foremost, FOSS is free. This means that you don’t have to pay for a license or subscription, which can be a huge cost savings. It also means that you don’t have to worry about license renewals or updates. All of the software is available for free and can be used indefinitely.

Second, FOSS is open source, which means that anyone can view and modify the code. This makes it easier for developers to customise the software to meet their needs, and it can be a great way to learn programming.

Third, FOSS is typically more secure than proprietary software. The code is open, so any potential security vulnerabilities can be identified and fixed quickly. Additionally, many of these programs are built by volunteers and have a large community of users who can help identify and fix bugs.

Finally, and slightly similar to the previous point, FOSS can have a faster turnaround of fixes than proprietary software. Since the code is open, developers are more likely to fix bugs and make improvements more quickly. This can mean fewer interruptions to your workflow.

Overall, free and open source software makes a lot of sense for individuals and businesses alike. With so many benefits, it’s easy to see why FOSS has become so popular.

Hidden costs to the convenience

Complex systems involve a lot of components which need to be maintained

Unfortunately, many people are not aware of the importance of upgrading their open source software. Upgrading the components of the system is essential for keeping your system secure, stable, and up to date. Over time, vulnerabilities are discovered in software packages and the only way to fix these vulnerabilities is to upgrade to the latest version. By not upgrading, you are essentially leaving your system open to malicious attacks. You are not taking advantage of a core FOSS benefit.

As outlined earlier, upgrading to the latest version of a software component can give you access to bug fixes and performance enhancements that make your system more reliable and efficient. Furthermore, new features can be added to software packages to make them even more powerful and useful. Finally, upgrading open source software can help you stay ahead of the competition. By keeping your software up to date, you can ensure that you are using the latest technologies and staying ahead of the curve. This can help you gain an edge over your competitors and give you a competitive advantage. In conclusion, upgrading open source software is essential for keeping your system secure, stable, and up to date. It also can give you access to bug fixes, performance enhancements, and new features. Finally, it can help you stay ahead of the competition and give you a competitive advantage. So make sure you keep your open source software up to date!

Oracle REST Data Services does rely on components from third parties including FOSS components and critical vulnerabilities and exploits are regularly monitored for known issues that would require an upgrade. Not only that, keeping on top of the security support commitments for those libraries. That is one of the reasons ORDS moved to Eclipse Jetty 10 last year. When a new version of ORDS is available, it is recommended to upgrade as soon as possible.

Why upgrade to Apache Tomcat 10.1 though?

With complex dependencies an upgrade is not always easy

Upgrading is easier said than done though. The interdependencies of the components in a software system can be quite complex. Upgrading to the latest version of one component could cause another component to no longer work. There is a tension between trying to keep existing systems running, introduce new applications / functionality and reduce complexity / costs in the runtime environment.

Which sets the scene for why we’re discussing Apache Tomcat 10.1. Why would someone want to use Apache Tomcat 10.1?

Support considerations

We can expect Tomcat versions released after 2007 to have around 10 years of support before they reach end of life. Tomcat 7, for example, was released in 2011 and reached end of life in 2021. Tomcat 9.0 was released in 2017 and declared stable in 2018. At the time of writing, version 9.0.71 was released earlier this month. No end of life date has been specified but one can expect it to be around 2027. With five years to go for new releases with fixes to bugs and security vulnerabilities there’s no pressing need to move off Tomcat 9.0. However, with Tomcat 8.5 scheduled for EOL at the end of March 2024 anyone currently using Tomcat 8.5 in production would be considering the stable release with the longest runway: Tomcat 10.1.

Jakarta

At this point you’ll note that Tomcat 10.0 does not get a mention. It has already reached it’s EOL and no further builds for that particular release will happen, irrespective of any CVE reported against it. The most likely motivation for moving to Tomcat 10.1 is because it is a Jakarta EE platform.  It builds on Tomcat 10.0.x and implements the Servlet 6.0JSP 3.1EL 5.0WebSocket 2.1 and Authentication 3.0 specifications (the versions required by Jakarta EE 10 platform). For businesses or individuals that deploy multiple applications there may be a conflict with wanting to use Jakarta Servlet API based web applications in the same infrastructure they have Javax Servlet API based web applications. The main difference between Javax Servlet API and Jakarta Servlet API is the addition of new features in the Jakarta version. Jakarta Servlet API adds features such as asynchronous servlet requests, Non-blocking IO, improved security, and better integration with other Jakarta EE APIs. Having one single version of Apache Tomcat to maintain would be preferable.

ORDS running on Apache Tomcat 10.1

Your Jakarta Servlet API based applications and ORDS on the same Apache Tomcat 10.1 !
Photo by Christina Morillo on Pexels.com

ORDS is a web application based on the Javax Servlet API and as such can not be deployed as a regular web application in Tomcat 10.1 which expects web applications to be implemented using Jakarta Servlet API. Thankfully, Apache has provided a comprehensive migration guide to help make transitioning from any version of Tomcat 9 to Tomcat 10 as simple as possible. In addition to that, there is a migration utility which converts the java byte code of the javax.servlet.HttpServlet classes to jakarta.servlet.http.HttpServlet classes. This is really useful because it makes it possible to deploy the generated ords.war using the Apache Tomcat migration tool for Jakarta EE.
https://tomcat.apache.org/migration-10.html#Specification_APIs

Referring to the Deploying ORDS on Apache Tomcat instructions it is simply a matter of generating the ords.war to the correct location:

ords --config /path/to/config war $CATALINA_HOME/webapps-javaee/ords.war

It really is as simple as that. This article was originally going to be a step by step guide but when you get down to it, there’s only one step!

Strictly speaking, Apache Tomcat 10.1 is not an officially supported release of Tomcat for deploying ORDS. At the time of writing it is Tomcat release 8.5.x through to release 9.0.x. Therefore, if there are any ORDS issues encountered the first thing you’ll be asked to do is see does the same issue occur with latest version of ORDS deployed on a recent Tomcat 9.0.x release.

Use of the Apache Tomcat migration tool for Jakarta EE to have ORDS running on Apache Tomcat 10.1 is worth considering if one has Jakarta Servlet API based web applications to deploy and only want a single version of Tomcat running.

Multiple ORDS instances in WebLogic Server or Apache Tomcat

UPDATE! As of ORDS 22.2.0 there is a command to generate web application for deployment to Apache Tomcat or Oracle WebLogic Server. See Deploying Oracle REST Data Services. The below article is informative and perhaps worth your time to get an understanding of web application deployment but there are supported, documented steps for achieving the same thing with the ORDS product. In fact, what is in the ORDS product is better because it also caters for jar content in the /lib/ext/ folder which this article does not cover.


ORDS 22.1.0 introduced a substantial set of changes: Java 11 as minimum, new command line interface and a new configuration directory structure. In fact it is a release that introduces a whole new deployment paradigm.

Deployment Options

What we knew as Standalone Mode in previous ORDS releases still exists and is now initiated through a serve command. The embedded Eclipse Jetty web server and servlet container engine is still at the heart of this convenient way to have ORDS listen HTTP/HTTPS traffic. The significant change is how the configuration directory is determined at startup. There is no longer a requirement to modify the distributed ords.war to set the configuration directory location.

Deployment on a supported servlet container, such as Oracle WebLogic Server or Apache Tomcat, is similarly impacted by this principle that the distributed ords.war should not be modified. A new mechanism for specifying the configuration directory when the application is deployed is required. The ORDS documentation covers the standard approach for a typical setup, use a Java System Property ( -Dconfig.url=/ords_config/ ), startup your server(s) and deploy the web application.

Single Server

For some developers that may not have a full production environment scaled out for high availability, or may be restricted in the Apache Tomcat or WebLogic Server deployment options, the approach to set different servers with their own ords.war and configuration is not always feasible. With previous ORDS releases they may have had the practice of deploying multiple ORDS web applications with different contexts and configurations. At first it would appear that the new direction in ORDS 22.1.0 makes that impossible but it is not. All that is required are a few extra steps.

Bake your own

For deploying to Apache Tomcat or WebLogic Server you can make your own, separate web application with the config.url baked in. Moreover, you can call it whatever makes sense in your context.

Heres a Python script to do that for you: create_deploy_war.py

This script produces a new web application archive file with the jars from the distributed ords.war and web.xml deployment descriptor has the configuration directory path set. That way, wherever it is deployed, it will refer to the specified configuration directory.

Usage: create_deploy_war <source ords.war> <destination war filename> <configuration directory>

For example, if I want to have two ORDS instances deployed called tom and jerry with two separate configuration directories and I have extracted the ORDS 22.1.0 to /opt/oracle/ords-22.1.0.105.1723/. The jerry web application will be configured with a default pool pointing to an Oracle 19c database. The tom web application will be configured with a default pool pointing to an Oracle 21c database. We will show both web applications have these configurations by using the REST Enabled SQL service in ORDS to return database version information. Before that, the web application files must first be created…

./create_deploy_war.py /opt/oracle/ords-22.1.0.105.1723/ords.war /scratch/ords_webapps/tom.jar /scratch/ords_configs/tom/
./create_deploy_war.py /opt/oracle/ords-22.1.0.105.1723/ords.war /scratch/ords_webapps/jerry.jar /scratch/ords_configs/jerry/

That will produce two web application war files in /scratch/ords_webapps/ and I can then deploy them…

Oracle WebLogic Server

Two ORDS web applications deployed to a single WebLogic Server
curl -u hr:hr http://localhost:7001/jerry/hr/_/sql

{
   "database_major_version" : 19,
   "database_minor_version" : 0,
   "database_product_name" : "Oracle",
   "database_product_version" : "Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production\nVersion 19.3.0.0.0",
   "env" : {
      "defaultTimeZone" : "GMT",
      "ordsVersion" : "22.1.0.r1051723"
   }
}
curl -u hr:hr http://localhost:7001/tom/hr/_/sql

{
   "database_major_version" : 21,
   "database_minor_version" : 0,
   "database_product_name" : "Oracle",
   "database_product_version" : "Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production\nVersion 21.3.0.0.0",
   "env" : {
      "defaultTimeZone" : "GMT",
      "ordsVersion" : "22.1.0.r1051723"
   }
}

As you can see from the above, the different web applications have different context paths and are configured to use two different databases.

Apache Tomcat

Two ORDS web applications deployed to Apache Tomcat
curl -u hr:hr http://localhost:8080/jerry/hr/_/sql

{
   "database_major_version" : 19,
   "database_minor_version" : 0,
   "database_product_name" : "Oracle",
   "database_product_version" : "Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production\nVersion 19.3.0.0.0",
   "env" : {
      "defaultTimeZone" : "GMT",
      "ordsVersion" : "22.1.0.r1051723"
   }
}
curl -u hr:hr http://localhost:8080/tom/hr/_/sql

{
   "database_major_version" : 21,
   "database_minor_version" : 0,
   "database_product_name" : "Oracle",
   "database_product_version" : "Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production\nVersion 21.3.0.0.0",
   "env" : {
      "defaultTimeZone" : "GMT",
      "ordsVersion" : "22.1.0.r1051723"
   }
}

Similar behaviour with Apache Tomcat with two different web applications with their own, separate, configuration.

Summary

What I have demonstrated is a script which produces a new web application for deployment in Apache Tomcat or WebLogic Server. The script is written for Python 2 because that’s the version distributed with Oracle WebLogic Server. It creates a new web application based on the distributed ords.war but without the META-INF content. That content includes a signature digest of the web.xml which will be invalid when the new deployment descriptor is written to the new WAR file.

This is what makes it possible to have multiple ORDS instances deployed to the same server all with different configuration directories.

HR Web Application – Tomcat & UCP

Many years ago a sample web application for using JDBC was published as part of the oracle-db-examples GitHub repository. The HR Web Application example was the starting point for some to build their first simple web interface to their database. Who knows how many simple, in-house applications have this as their inspiration?

The example had a particular focus on Apache Tomcat and the steps to getting it built and deployed where simple for the standard Apache Tomcat setup at the time.

-- Get the code
git clone https://github.com/oracle/oracle-db-examples.git
cd oracle-db-examples/java/HRWebApp
-- Copy the tomcat-users.xml and start tomcat
cp tomcat-users.xml $CATALINA_HOME/conf
catalina.sh start
-- Build the war file and deploy it
mvn package
cp target/JdbcWebSamples.war $CATALINA_HOME/webapps

By copying the JdbcWebSamples.war into the $CATALINA_HOME/webapps directory, the web application is automatically deployed by Apache Tomcat. The context path is based on the file name so the URL is http://localhost:8080/JdbcWebSamples/

Use your browser to access the web application.

The tomcat-users.xml defined two new users: hradmin and hrstaff. Both have welcome as their password. Login and click on the List All menu item to see the list of HR.EMPLOYEES records. Of course, that’s if the database connection details are correct.

Your JdbcBeanImpl.java will have to be changed to point to the correct database in the getConnection() method.

 public static Connection getConnection() throws SQLException {
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
    Connection connection = DriverManager.getConnection(
         "jdbc:oracle:thin:@//mydatabaseserver:1521/orclpdb1", 
         "hr", 
         "hr");

    return connection;
  }

Make the above change for your database, run mvn package and copy the JdbcWebSamples.war to $CATALINA_HOME/webapps to get that running with the new connection details.

Take a look through JdbcBeanImpl methods in detail and you’ll notice that getConnection() method is called using the try-with-resource syntax so the connection is automatically closed after every operation. It’s a good practice because it does not leave INACTIVE sessions on the database. In fact, if you check your database v$session you shouldn’t find any records.

SELECT
    *
FROM
    v$session
WHERE
    program = 'JDBC Thin Client'
    AND schemaname = 'HR';

Creating a database connection for every request does have an overhead and if that is a remote database the network latency could be sufficient to cause problems for the users of the web application. To see the impact of creating a connection every time, let’s add some basic logging on the elapsed time to that getConnection() method.

 public static Connection getConnection() throws SQLException {
    final long start = System.currentTimeMillis();
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
    Connection connection = DriverManager.getConnection(
         "jdbc:oracle:thin:@//mydatabaseserver:1521/orclpdb1", 
         "hr", 
         "hr");
    final long end = System.currentTimeMillis();
    logger.log(Level.INFO, 
         "Creating a connection duration(ms): " + (end - start));

    return connection;
  }

Like before, make the above change for your database, run mvn package and copy the JdbcWebSamples.war to $CATALINA_HOME/webapps. Every time the list of employees is retrieved, or any action that involves the connection, the cataline.out will show a log message.

Creating a connection to my remote database takes over 2 seconds!

One way to address this is by using Oracle’s Universal Connection Pool. This could be used through a data source defined in the Tomcat configuration but it can also be done used programmatically. Let’s do that.

The first thing is to upgrade to a more recent version of JDBC/UCP. Not the latest 21.1 version, more on that later, but we’ll use 19.9.0.0 for now. Note that the group id has changed from com.oracle.jdbc to com.oracle.database.jdbc and that’s what we’ll use in the pom.xml

Change this

    <dependency>
      <groupId>com.oracle.jdbc</groupId>
      <artifactId>ojdbc8</artifactId>
      <version>12.2.0.1</version>
    </dependency>

to this

    <dependency>
      <groupId>com.oracle.database.jdbc</groupId>
      <artifactId>ojdbc8</artifactId>
      <version>19.9.0.0</version>
    </dependency>
    <dependency>
      <groupId>com.oracle.database.jdbc</groupId>
      <artifactId>ucp</artifactId>
      <version>19.9.0.0</version>
    </dependency>

Now add a class to create and configure a Universal Connection Pool PoolDataSource. Let’s call it JdbcSource in the com.oracle.jdbc.samples.bean package.

package com.oracle.jdbc.samples.bean;

import java.sql.Connection;
import java.sql.SQLException;

import java.util.logging.Level;
import java.util.logging.Logger;

import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;

public class JdbcSource {
  JdbcSource() {

    //Create pool-enabled data source instance.
    this.pds = PoolDataSourceFactory.getPoolDataSource();

    //set the connection properties on the data source.
    try {
      pds.setConnectionPoolName(POOL_NAME);
      pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
      pds.setURL("jdbc:oracle:thin:@//mydatabaseserver:1521/orclpdb1");
      pds.setUser("hr");
      pds.setPassword("hr");

      //Override any pool properties.
      pds.setInitialPoolSize(2);

    } catch (SQLException ex) {
      logger.log(Level.SEVERE, null, ex);
      ex.printStackTrace();
    }
  }

  public Connection connection() throws SQLException {
      return this.pds.getConnection();
  }

  private final PoolDataSource pds;
  private static final String POOL_NAME = "JdbcWebSamples_pool";

  // Singleton data source. Not a great pattern but simple for demonstrations.
  public static JdbcSource INSTANCE = new JdbcSource();
  static final Logger logger = Logger.getLogger("com.oracle.jdbc.samples.bean.JdbcSource");
}

The above will create a pool of database connections that can be reused every time connection() method is called. The pool is initialised with 2 connections. That means there will be two sessions on the database that will be INACTIVE most of the time. The web application may deal with concurrent requests so having an extra connection ready will help with that additional load.

Let’s revisit that JdbcBeanImpl getConnection() method and change it to use the new pooled connection. It’s not all that complicated…

 public static Connection getConnection() throws SQLException {
    final long start = System.currentTimeMillis();
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
    Connection connection = JdbcSource.INSTANCE.connection();
    final long end = System.currentTimeMillis();
    logger.log(Level.INFO, 
         "Creating a connection duration(ms): " + (end - start));

    return connection;
  }

Like before, make the above for your code, run mvn package and copy the JdbcWebSamples.war to $CATALINA_HOME/webapps. Everytime the list of employees is retrieved, or any action that involves the connection, the cataline.out will show a log message for the first connection taking a long time, but every subsequent call takes milliseconds.

There’s an overhead for the 1st connection but subsequent requests get a connection in milliseconds

The query on v$sessions will show 2 INACTIVE sessions at least. As more concurrent requests are received, the pool size will grow automatically so more sessions could be created. In fact, hit that List All menu item repeatedly 20 or 30 times and you’ll see the number of v$sessions for HR schema grow. There’s more to explore here on setting UCP properties for optimising pool behaviour.

Upgrade to JDBC / UCP 21.1

Let’s leave that as a bonus point exercise for you and discuss upgrading to 21.1.0.0. This version has support for defining data sources in JBoss and Spring. That enhancement does break these example web applications which include the UCP jars in them.

Update the pom.xml to use the new 21.1.0.0 JDBC and UCP jars.

    <dependency>
      <groupId>com.oracle.database.jdbc</groupId>
      <artifactId>ojdbc8</artifactId>
      <version>21.1.0.0</version>
    </dependency>
    <dependency>
      <groupId>com.oracle.database.jdbc</groupId>
      <artifactId>ucp</artifactId>
      <version>21.1.0.0</version>
    </dependency>

Rebuild and deploy to see the following SEVERE error messages.

SEVERE [Catalina-utility-1] org.apache.catalina.core.StandardContext.startInternal One or more listeners failed to start. Full details will be found in the appropriate container log file
SEVERE [Catalina-utility-1] org.apache.catalina.core.StandardContext.startInternal Context [/JdbcWebSamples] startup failed due to previous errors

The localhost log will have more details.

SEVERE [Catalina-utility-1] org.apache.catalina.core.StandardContext.listenerStart Error configuring application listener of class [oracle.ucp.jdbc.UCPServletContextListener]
	java.lang.NoSuchMethodException: oracle.ucp.jdbc.UCPServletContextListener.<init>()
		at java.base/java.lang.Class.getConstructor0(Class.java:3427)
		at java.base/java.lang.Class.getConstructor(Class.java:2165)
		at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:151)
		at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4607)
		at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5146)
		at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
		at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:717)
		at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:690)
		at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:705)
		at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:978)
		at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1849)
		at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
		at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
		at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
		at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118)
		at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:773)
		at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:427)
		at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1620)
		at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:305)
		at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
		at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1151)
		at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1353)
		at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1357)
		at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1335)
		at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
		at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
		at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
		at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
		at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
		at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
		at java.base/java.lang.Thread.run(Thread.java:832)
SEVERE [Catalina-utility-1] org.apache.catalina.core.StandardContext.listenerStart Skipped installing application listeners due to previous error(s)

Put simply there is an annotated class that Tomcat discovers in the web application classpath and tries to initialise it, and it fails. It’s not a great work around, but a quick fix is to use the web.xml to tell Tomcat the names of components it should attempt to initialise. In this example web application there are two servlets defined: GetRole and WebController. These are in the com.oracle.jdbc.samples.web package. We’ll mention their names explicitly in the web.xml. Add this absolute-ordering entry just after the login-config.

  </login-config>
  <absolute-ordering>
    <name>GetRole</name>
    <name>WebController</name>
  </absolute-ordering>
</web-app>

It may not be a feasible work around for all, and it sort of defeats the purpose of having annotated servlets, but explicitly mentioning their names in absolute-ordering makes upgrading the JDBC/UCP jars possible.