Java Приложение Spring Boot с несколькими драйверами базы данных не работает на Linux - PullRequest
3 голосов
/ 27 марта 2020

Этот вопрос НЕ касается настройки нескольких источников данных.

Мы выполняем миграцию из базы данных A (тип: Sybase) в базу данных B (тип: MS SQL), но временно нуждаемся в поддержке обоих, пока продолжается эта миграция. Таблицы / схемы в этих двух базах данных идентичны. В приложении Spring Boot 2.X Java 11 я хочу гибко определить, какую базу данных использовать при развертывании приложения, реализовав всю конфигурацию источника данных в двух разных профилях. Это прекрасно работает при локальном запуске (IntelliJ / Windows). Запуск maven clean install как внутри, так и снаружи IntelliJ работает нормально.

Для модульных тестов мы используем dbunit с H2 в базе данных памяти. Это требует дополнительного драйвера. Все модульные тесты успешно проходят локально (IntelliJ или Maven / Windows). При запуске модульных тестов основная конфигурация приложения не используется, и вся конфигурация источника данных является 100% -ной конфигурацией dbunit.

Так что все выглядит в порядке, верно? К сожалению, когда наш сервер сборки Jenkins (Linux! ) запускает юнит-тест dbunit, HikariPool не может нормально запуститься для тех тестов, которым он требуется, из-за этой довольно криптиевой c ошибки:

2020-03-26 17:56:27.366  INFO 15845 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-7 - Starting...
2020-03-26 17:56:27.369  WARN 15845 --- [           main] o.s.w.c.s.GenericWebApplicationContext   : Exception encountered during context initialization ...
2020-03-26 17:56:27.380 ERROR 15845 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException:
                Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration':
                                Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException:
                                                Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]:
                                                                Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException:
                                                                                Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker': 
                                                                                                Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: 
                                                                                                                Could not initialize class sun.security.provider.NativePRNG

В моем файле pom.xml есть три зависимости для этих драйверов (sybase, ms sql и h2). Если я удаляю sybase ИЛИ зависимость ms sql (таким образом, есть только один драйвер + драйвер базы данных H2), настройка базы данных будет успешной, и все модульные тесты будут запущены / пройдены в Jenkins.

Этот класс, который предположительно не найдено существует ли в Java 11 JDK, которые мы используем (локально это один для windows, на Jenkins это тот же самый jdk, но для linux).

Некоторые вопросы, которые у меня сейчас есть:

  • Я совершенно заблудился относительно того, почему источник данных требует этот класс, и только на linux машинах.
  • Этот класс является пустой оболочкой на windows, но есть реализация linux (api здесь ). Что этот класс пытается сделать?
  • Почему этот класс не может загрузить его должным образом.
  • Я также не понимаю, почему эта проблема возникает только тогда, когда на пути к классам находятся несколько драйверов, но не возникает, когда присутствует только один.

Я нашел только одна запись блога об этой топике c с недокументированным утверждением о том, что Spring не поддерживает несколько драйверов:

Одна из проблем, связанных с Spring, заключается в том, что вы на самом деле не можете использовать несколько драйверов базы данных в одном пакете. Это просто ограничение, которое имеет Spring. Но, к счастью для нас, кто-то, намного умнее меня, понял, как вы можете сделать следующее лучшее: отдельные репозитории с различными драйверами базы данных.

Я думаю, что это неверно, потому что приложение работает нормально. Я не совсем понимаю, почему сборка на Дженкинсе испортила юнит-тесты. Решение, представленное в этом посте, не будет работать для меня, потому что у меня есть только 1 пакет с 1 logi c, мне не нужны эти несколько драйверов для доступа к нескольким источникам данных.

1 Ответ

3 голосов
/ 27 марта 2020

Ваша проблема необычна и нелегко воспроизвести, поэтому у меня нет проверенного решения, но есть важный совет.

Прежде всего, распространенная ошибка: NoClassDefFoundError не происходит, только когда класс не находится в classpath, но также и в случае непроверенного исключения, выданного инициализатором класса stati c. Этот факт не сообщается в Javado c, но может быть легко продемонстрирован с помощью следующего кода:

public class Demo {

    public static void callMe() {
        System.out.println("Called");
    }

    static {
        if (true) {
            System.err.println("Going to fail");
            throw new NullPointerException();
        }
    }
}


public class Main {

    public static void main(String[] args) {
        try {
            Demo.callMe();
        } catch(Throwable e) {
            e.printStackTrace();  //Log ExceptionInInitializerError and continue
        }
        System.err.println("2nd call");
        Demo.callMe();  //NoClassDefFoundError here
    }

}

NativePRNG почти пуст в Windows, но реализация Linux во время загрузки классов выполняется много кода, и этот код обращается к /dev/random и / или /dev/urandom, поэтому многие вещи могут go ошибаться. К счастью, в нем есть запись, что можно активировать с помощью

-Djava.security.debug="provider,engine=SecureRandom"

, и это может помочь в устранении неполадок.

Другой вариант: этот пост объясняет, что вы можете изменить стандартную безопасную случайную реализацию. Возможно, так называемая реализация SHA1PRNG работает.

Ошибка появляется, когда у вас есть и sybase, и драйвер ms sql в classpath, но во время модульных тестов, и вы говорите, что в тестах вы используете только H2. Обратите внимание, что современные драйверы JDB C (v4.0 +) автоматически обнаруживаются и автоматически загружаются загрузчиком классов (старый трюк Class.forName() больше не нужен, см. Учебник Java ). Любой драйвер JDB C имеет инициализатор stati c, который, по крайней мере, уведомляет DriverManager о его существовании, но также может получить доступ к NativePRNG.

Теперь некоторые предположения. В моем примере выше вы получаете NoClassDefFoundError на втором доступе к классу. Я предполагаю, что первый драйвер, который пытается использовать этот класс, получает ExceptionInInitializerError и молча игнорирует его (возможно, он возвращается к Math.random()), затем второй (или 3-й) драйвер пытается сделать то же самое, но получает NoClassDefFoundError и выходит из строя. Это будет ошибка в драйвере (она должна игнорировать NoClassDefFoundError тоже). Таким образом, в случае сбоя любого другого параметра вы можете попытаться использовать другую версию (более новую или даже более старую) этих драйверов, а также другую версию Hikari или другой пул БД.

...