Индивидуальный источник данных Tomcat JNDI - PullRequest
0 голосов
/ 07 ноября 2018

У меня есть несколько веб-приложений, которые используют одну базу данных. До недавнего времени я использовал источник данных JNDI примерно так:

server.xml:

<Resource name="jdbc/dbPool" auth="Container" type="javax.sql.DataSource"
          maxActive="100" minIdle="10" maxIdle="30" maxWait="1000" 
          username="username" password="password" 
          driverClassName="oracle.jdbc.OracleDriver"
          factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          url="jdbc:oracle:thin:@//localhost:1521/XE"/>

context.xml:

<ResourceLink name="jdbc/service1DB" global="jdbc/dbPool" type="javax.sql.DataSource"/>
<ResourceLink name="jdbc/service2DB" global="jdbc/dbPool" type="javax.sql.DataSource"/>
<ResourceLink name="jdbc/service3DB" global="jdbc/dbPool" type="javax.sql.DataSource"/>
<ResourceLink name="jdbc/service4DB" global="jdbc/dbPool" type="javax.sql.DataSource"/>

Конфигурация сервера:

@Bean
public DataSource dataSource(String dataSourceJndiName) {
    JndiDataSourceLookup lookup = new JndiDataSourceLookup();
    lookup.setResourceRef(true);
    DataSource dataSource;
    try {
        dataSource = lookup.getDataSource(dataSourceJndiName);
    } catch (DataSourceLookupFailureException e) {
        log.error("Cannot establish database connection", e)
        throw e;
    }
    return dataSource;
}

Теперь мне нужно начать настройку диалекта базы данных (который до сих пор был жестко задан) в ресурсе JNDI. Мои конфигурации Tomcat и сервера теперь выглядят так:

server.xml:

<Resource name="jdbc/dbPool" auth="Container" type="my.webapp.CustomDataSource"
          maxActive="100" minIdle="10" maxIdle="30" maxWait="1000" 
          username="username" password="password" 
          driverClassName="oracle.jdbc.OracleDriver"
          factory="my.webapp.CustomDataSourceFactory"
          dialect="org.hibernate.dialect.Oracle10gDialect"
          url="jdbc:oracle:thin:@//localhost:1521/XE"/>

context.xml:

<ResourceLink name="jdbc/service1DB" global="jdbc/dbPool" type="my.webapp.CustomDataSource"/>
<ResourceLink name="jdbc/service2DB" global="jdbc/dbPool" type="my.webapp.CustomDataSource"/>
<ResourceLink name="jdbc/service3DB" global="jdbc/dbPool" type="my.webapp.CustomDataSource"/>
<ResourceLink name="jdbc/service4DB" global="jdbc/dbPool" type="my.webapp.CustomDataSource"/>

Реализация CustomDataSourceFactory (важные материалы скопированы из org.apache.tomcat.jdbc.pool.DataSourceFactory с сжатым диалектом):

public class CustomDataSourceFactory extends DataSourceFactory {

    private static final String PROP_DIALECT = "dialect";
    private static final String[] CUSTOM_PROPERTIES = new String[]{PROP_DIALECT};
    private static final String[] PROPERTIES = ArrayUtils.addAll(ALL_PROPERTIES, CUSTOM_PROPERTIES);

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        if (obj != null && obj instanceof Reference) {
            Reference ref = (Reference) obj;
            Properties properties = new Properties();

            for (int i = 0; i < PROPERTIES.length; ++i) {
                String propertyName = PROPERTIES[i];
                RefAddr ra = ref.get(propertyName);
                if (ra != null) {
                    String propertyValue = ra.getContent().toString();
                    properties.setProperty(propertyName, propertyValue);
                }
            }

            return this.createDataSource(properties, nameCtx);
        } else {
            return null;
        }
    }

    public javax.sql.DataSource createDataSource(Properties properties, Context context) throws Exception {
        PoolConfiguration poolProperties = parsePoolProperties(properties);
        if (poolProperties.getDataSourceJNDI() != null && poolProperties.getDataSource() == null) {
            this.performJNDILookup(context, poolProperties);
        }

        String dialect = properties.getProperty(PROP_DIALECT);
        if (dialect == null) {
            log.error("Dialect is unspecified");
            return null;
        }

        CustomDataSource dataSource = new CustomDataSource(dialect, poolProperties);
        dataSource.createPool();
        return dataSource;
    }
}

Реализация CustomDataSource (расширяет org.apache.tomcat.jdbc.pool.DataSource):

@NoArgsConstructor
public class CustomDataSource extends DataSource {

    @Getter
    @Setter
    private String dialect;

    public CustomDataSource(String dialect, PoolConfiguration poolProperties) {
        super(poolProperties);
        this.dialect = dialect;
    }
}

Это несколько работает - когда я перезагружаю Tomcat, первое веб-приложение (например, использующее источник данных jdbc / service1DB) запускается успешно, использует настроенный диалект и работает нормально, но все остальные не работают с ошибкой при поиске источника данных. Кроме того, при развертывании веб-приложений без перезапуска Tomcat возникает та же ошибка (даже с первым веб-приложением, которое ранее успешно запускалось после перезапуска Tomcat):

"org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException":"Failed to look up JNDI DataSource with name 'java:comp/env/jdbc/service2DB'; nested exception is javax.naming.NamingException: The local resource link [service2DB] that refers to global resource [jdbc/dbPool] was expected to return an instance of [my.webapp.CustomDataSource] but returned an instance of [my.webapp.CustomDataSource]"

В чем здесь проблема?

1 Ответ

0 голосов
/ 07 ноября 2018

Ааа, я понял это.

Я заметил, что когда-либо был создан только один «экземпляр» CustomDataSource - после перезапуска в журналах было небольшое указание на то, что источник данных был создан, а затем запущено первое веб-приложение, но ни для одного из источников данных не было создано снова. веб-приложения до следующей перезагрузки.

Я перечитал главу «Добавление пользовательских фабрик ресурсов» в разделе ресурсов JNDI Tomcat, и заметил комментарий об установке атрибута singleton на false (я читал его раньше, но не думал, что это важно для моего дело). Я добавил его в свой источник данных:

<Resource name="jdbc/dbPool" auth="Container" type="my.webapp.CustomDataSource"
      maxActive="100" minIdle="10" maxIdle="30" maxWait="1000" 
      username="username" password="password" 
      driverClassName="oracle.jdbc.OracleDriver"
      factory="my.webapp.CustomDataSourceFactory"
      dialect="org.hibernate.dialect.Oracle10gDialect"
      url="jdbc:oracle:thin:@//localhost:1521/XE"
      singleton="false"/>

И это работает! Все мои веб-приложения запускаются и используют настроенный мной диалект!

...