tl;dr
How do I tell Tomcat 9 to use a Postgres-specific object factory for producing DataSource object in response to JNDI query?
Details
I can easily get a DataSource object from Apache Tomcat 9 by defining an XML file named the same as my context. For example, for a web-app named clepsydra, I create this file:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <!-- Domain: DEV, TEST, ACPT, ED, PROD  -->
    <Environment name = "work.basil.example.deployment-mode"
                 description = "Signals whether to run this web-app with development, testing, or production settings."
                 value = "DEV"
                 type = "java.lang.String"
                 override = "false"
                 />
    <Resource
                name="jdbc/postgres"
                auth="Container"
                type="javax.sql.DataSource"
                driverClassName="org.postgresql.Driver"
                url="jdbc:postgresql://127.0.0.1:5432/mydb"
                user="myuser"
                password="mypasswd"
                />
</Context>
I place that file in my Tomcat “base” folder, in conf folder, in folders I created with engine name Catalina and host name localhost. Tomcat feeds settings into a resource factory to return an instance of DataSource. I can access that instance via JNDI:
Context ctxInitial = new InitialContext();
DataSource dataSource = 
        ( DataSource ) ctxInitial.lookup( "java:comp/env/jdbc/postgres" )
;
I do realize that postgres in that lookup string could be something more specific to a particular app. But let's go with postgres for the same of demonstration.
I want org.postgresql.ds.PGSimpleDataSource, not org.apache.tomcat.dbcp.dbcp2.BasicDataSource
This setup is using Tomcat’s own resource factory for JDBC DataSource objects. The underlying class of the returned DataSource class is org.apache.tomcat.dbcp.dbcp2.BasicDataSource. Unfortunately, I do not want a DataSource of that class. I want a DataSource of the class provided by the JDBC driver from The PostgreSQL Global Development Group: org.postgresql.ds.PGSimpleDataSource.  
By reading the Tomcat documentation pages, JNDI Resources How-To and JNDI Datasource How-To, I came to realize that Tomcat allows us to use an alternate factory for these DataSource objects in place of the default factory implementation bundled with Tomcat. Sounds like what I need.
PGObjectFactory
I discovered that the Postgres JDBC driver already comes bundled with such implementations:
PGObjectFactory
For simple JDBC connections.PGXADataSourceFactory
For XA-enabledDataSourceimplementation, for distributed transactions.
By the way, there is a similar factory built into the driver for OSGi apps, PGDataSourceFactory. I assume that is of no use to me with Tomcat.
So, the PGObjectFactory class implements the interface javax.naming.spi.ObjectFactory required by JNDI. 
SPI
I am guessing that the spi in that package name means the object factories load via the Java Service Provider Interface (SPI). 
So I presume that need a SPI mapping file, as discussed in the Oracle Tutorial and in the Vaadin documentation. added a META-INF folder to my Vaadin resources folder, and created a services folder further nested there. So in /resources/META-INF/services I created a file named javax.naming.spi.ObjectFactory containing a single line of text, the name of my desired object factory: org.postgresql.ds.common.PGObjectFactory. I even checked inside the Postgres JDBC driver to verify physically the existence and the fully-qualified name of this class.
Question
➥ My question is: How do I tell Tomcat to use PGObjectFactory rather than its default object factory for producing my DataSource objects for producing connections to my Postgres database?
factory attribute on <Resource> element
I had hoped it would be as simple as adding a factory attribute (factory="org.postgresql.ds.common.PGObjectFactory") to my <Resource> element seen above. I got this idea from the Tomcat page, The Context Container. That page is quite confusing as it focuses on global resource, but I do not need or want to define this DataSource globally. I need this DataSource only for my one web app. 
Adding that factory attribute:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <!-- Domain: DEV, TEST, ACPT, ED, PROD  -->
    <Environment name = "work.basil.example.deployment-mode"
                 description = "Signals whether to run this web-app with development, testing, or production settings."
                 value = "DEV"
                 type = "java.lang.String"
                 override = "false"
                 />
    <Resource
                name="jdbc/postgres"
                auth="Container"
                type="javax.sql.DataSource"
                driverClassName="org.postgresql.Driver"
                url="jdbc:postgresql://127.0.0.1:5432/mydb"
                user="myuser"
                password="mypasswd"
                factory="org.postgresql.ds.common.PGObjectFactory"
                />
</Context>
…fails with my DataSource object being null.
ctxInitial = new InitialContext();
DataSource dataSource = ( DataSource ) ctxInitial.lookup( "java:comp/env/jdbc/postgres" );
System.out.println( "dataSource = " + dataSource );
null
Removing that factory="org.postgresql.ds.common.PGObjectFactory" attribute resolves the exception. But then I am back to getting a Tomcat BasicDataSource rather than a Postgres PGSimpleDataSource. Thus my Question here.
I know my Context XML is being loaded successfully because I can access that Environment entry’s value.
2nd experiment
I tried this from the top, days later.
I created a new "Plain Java Servlet" flavor Vaadin 14.0.9 project named "datasource-object-factory".
Here is my entire Vaadin web app code. The bottom half is the JNDI lookup.
package work.basil.example;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.PWA;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/**
 * The main view contains a button and a click listener.
 */
@Route ( "" )
@PWA ( name = "Project Base for Vaadin", shortName = "Project Base" )
public class MainView extends VerticalLayout
{
    public MainView ( )
    {
        Button button = new Button( "Click me" ,
                event -> Notification.show( "Clicked!" ) );
        Button lookupButton = new Button( "BASIL - Lookup DataSource" );
        lookupButton.addClickListener( ( ClickEvent < Button > buttonClickEvent ) -> {
            Notification.show( "BASIL - Starting lookup." );
            System.out.println( "BASIL - Starting lookup." );
            this.lookupDataSource();
            Notification.show( "BASIL - Completed lookup." );
            System.out.println( "BASIL - Completed lookup." );
        } );
        this.add( button );
        this.add( lookupButton );
    }
    private void lookupDataSource ( )
    {
        Context ctxInitial = null;
        try
        {
            ctxInitial = new InitialContext();
            // Environment entry.
            String deploymentMode = ( String ) ctxInitial.lookup( "java:comp/env/work.basil.example.deployment-mode" );
            Notification.show( "BASIL - deploymentMode: " + deploymentMode );
            System.out.println( "BASIL - deploymentMode = " + deploymentMode );
            // DataSource resource entry.
            DataSource dataSource = ( DataSource ) ctxInitial.lookup( "java:comp/env/jdbc/postgres" );
            Notification.show( "BASIL - dataSource: " + dataSource );
            System.out.println( "BASIL - dataSource = " + dataSource );
        }
        catch ( NamingException e )
        {
            Notification.show( "BASIL - NamingException: " + e );
            System.out.println( "BASIL - NamingException: " + e );
            e.printStackTrace();
        }
    }
}
To keep things simple, I did not designate a Tomcat "base" folder, instead going with defaults. I did not run from IntelliJ, instead moving my web app’s WAR file manually to the webapps folder.
I downloaded a new version of Tomcat, version 9.0.27. I dragged in the Postgres JDBC jar to the /lib folder. I used the BatChmod app to set the permissions of the Tomcat folder. 
To the conf folder, I created the Catalina & localhost folders. In there I created a file named datasource-object-factory.xml with the same contents as seen above.
<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <!-- Domain: DEV, TEST, ACPT, ED, PROD  -->
    <Environment name = "work.basil.example.deployment-mode"
                 description = "Signals whether to run this web-app with development, testing, or production settings."
                 value = "DEV"
                 type = "java.lang.String"
                 override = "false"
                 />
    <Resource
                factory="org.postgresql.ds.common.PGObjectFactory"
                name="jdbc/postgres"
                auth="Container"
                type="javax.sql.DataSource"
                driverClassName="org.postgresql.Driver"
                url="jdbc:postgresql://127.0.0.1:5432/mydb"
                user="myuser"
                password="mypasswd"
                />
</Context>
I copied my web app’s datasource-object-factory.war file to webapps in Tomcat. Lastly, I run Tomcat's /bin/startup.sh and watch the WAR file explode into a folder.
With the factory="org.postgresql.ds.common.PGObjectFactory" attribute on my Resource element, the resulting DataSource is null. 
As with my first experiment, I can access the value of the <Environment>, so I know my context-name XML file is being found and processes successfully via JNDI.
Here are the logs on a Google Drive:
