tl; dr
Как мне указать Tomcat 9 использовать Postgres-специфическую фабрику объектов для производства DataSource
объектав ответ на JNDI запрос?
Подробности
Я могу легко получить DataSource
объект от Apache Tomcat 9определив файл XML, названный так же, как мой контекст. Например, для веб-приложения с именем clepsydra
я создаю этот файл:
<?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"
username="myuser"
password="mypasswd"
/>
</Context>
Я помещаю этот файл в свою «базовую» папку Tomcat, в папку conf
, в папки, созданные с помощьюимя двигателя Catalina
и имя хоста localhost
. Tomcat передает настройки в фабрику ресурсов, чтобы вернуть экземпляр DataSource
. Я могу получить доступ к этому экземпляру через JNDI:
Context ctxInitial = new InitialContext();
DataSource dataSource =
( DataSource ) ctxInitial.lookup( "java:comp/env/jdbc/postgres" )
;
Я понимаю, что postgres
в этой строке поиска может быть чем-то более специфичным для конкретного приложения. Но давайте пойдем с postgres
для той же демонстрации.
Я хочу org.postgresql.ds.PGSimpleDataSource
, а не org.apache.tomcat.dbcp.dbcp2.BasicDataSource
Эта установка использует собственную фабрику ресурсов Tomcat для объектов JDBC DataSource. Базовый класс возвращенного класса DataSource
равен org.apache.tomcat.dbcp.dbcp2.BasicDataSource
. К сожалению, я не хочу DataSource
этого класса. Мне нужен DataSource
класса, предоставляемого драйвером JDBC от Глобальной группы разработки PostgreSQL : org.postgresql.ds.PGSimpleDataSource
.
Прочитав страницы документации Tomcat, How-To ресурсов JNDI и How-To источника данных JNDI , Iпришел к выводу, что Tomcat позволяет использовать альтернативную фабрику для этих DataSource
объектов вместо стандартной реализации фабрики, связанной с Tomcat. Звучит так, как мне нужно.
PGObjectFactory
Я обнаружил, что драйвер Postgres JDBC уже поставляется в комплекте с такими реализациями:
Кстати, в драйвер для приложений OSGi встроена аналогичная фабрика, PGDataSourceFactory
. Я предполагаю, что это бесполезно для меня с Tomcat.
Итак, класс PGObjectFactory
реализует интерфейс javax.naming.spi.ObjectFactory
, требуемый JNDI.
SPI
Я предполагаю, что spi
в этом имени пакета означает загрузку фабрик объектов через Интерфейс поставщика услуг Java (SPI) .
Поэтому я предполагаю, что нужен файл сопоставления SPI, как обсуждалось в Oracle Tutorial и в Vaadinдокументация . добавили папку META-INF
в мою папку Vaadin resources
и создали папку services
, которая была дополнительно вложена в нее. Итак, в /resources/META-INF/services
я создал файл с именем javax.naming.spi.ObjectFactory
, содержащий одну строку текста, имя моей желаемой фабрики объектов: org.postgresql.ds.common.PGObjectFactory
. Я даже проверил внутри драйвера Postgres JDBC, чтобы физически проверить существование и полное имя этого класса.
Вопрос
➥ Мой вопрос: как мне сказать Tomcat:использовать PGObjectFactory
вместо фабрики объектов по умолчанию для создания моих DataSource
объектов для создания соединений с моей базой данных Postgres?
factory
атрибут <Resource>
элемент
Iнадеялся, что это будет так же просто, как добавление атрибута factory
(factory="org.postgresql.ds.common.PGObjectFactory"
) к моему элементу <Resource>
, показанному выше. Я получил эту идею со страницы Tomcat, Контейнер контекста . Эта страница довольно запутанная, поскольку она фокусируется на глобальном ресурсе, но мне не нужно или не нужно определять это DataSource
глобально. Мне нужно это DataSource
только для одного моего веб-приложения.
Добавление этого атрибута factory
:
<?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"
username="myuser"
password="mypasswd"
factory="org.postgresql.ds.common.PGObjectFactory"
/>
</Context>
... завершается с ошибкой, когда мой DataSource
объект является нулевым.
ctxInitial = new InitialContext();
DataSource dataSource = ( DataSource ) ctxInitial.lookup( "java:comp/env/jdbc/postgres" );
System.out.println( "dataSource = " + dataSource );
null
Удаление этого атрибута factory="org.postgresql.ds.common.PGObjectFactory"
разрешает исключение. Но потом я снова получаю Tomcat BasicDataSource
, а не Postgres PGSimpleDataSource
. Таким образом, мой вопрос здесь.
Я знаюТеперь мой Context
XML успешно загружается, потому что я могу получить доступ к значению этой записи Environment
.
2-й эксперимент
Я попробовал это сверху, несколько дней спустя.
Я создал новый проект Vaadin 14.0.9 "Простой Java-сервлет" под названием "datasource-object-factory".
Вот весь мой код веб-приложения Vaadin. Нижняя часть - это поиск JNDI.
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();
}
}
}
Для простоты я не определял "базовую" папку Tomcat, а использовал значения по умолчанию. Я не запускался из IntelliJ, вместо этого вручную перемещая файл WAR моего веб-приложения в папку webapps
.
Я скачал новую версию Tomcat, версия 9.0.27. Я перетащил JAR-файл Postgres в папку /lib
. Я использовал приложение BatChmod, чтобы установить разрешения для папки Tomcat.
В папке conf
я создал папки Catalina
& localhost
. Там я создал файл с именем datasource-object-factory.xml
с таким же содержимым, как показано выше.
<?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"
username="myuser"
password="mypasswd"
/>
</Context>
Я скопировал файл datasource-object-factory.war
моего веб-приложения в webapps
в Tomcat. Наконец, я запускаю Tomcat /bin/startup.sh
и наблюдаю, как файл WAR взрывается в папке.
С атрибутом factory="org.postgresql.ds.common.PGObjectFactory"
в моем элементе Resource
получается DataSource
null
.
Как и в моем первом эксперименте, я могу получить доступ к значению <Environment>
, поэтому я знаю, что мой XML-файл контекста-имени найден и успешно обрабатывается через JNDI.
Вотрегистрируется на Google Диске: