sbt-assembly нарушает импорт библиотеки в jar

Вопрос или проблема

Я пытаюсь перейти с использования MySQL Connector/J на MariaDB Connector/J 2.7.12 в проекте на Scala 2.12. После недели подозрений в баге в драйвере MariaDB, я обнаружил, что настоящая проблема заключается в том, что логика объединения для sbt-assembly заменяет библиотеки, из-за чего драйвер MariaDB выдает ошибку при подключении к MySQL 8.

Ошибка, о которой идет речь:

java.sql.SQLNonTransientConnectionException: Не удалось подключиться к address=(host=192.168.240.7)(port=3306)(type=master) : Клиент не поддерживает протокол аутентификации, запрошенный сервером. тип плагина был="caching_sha2_password"
  в org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73)

Я знаю, что это не драйвер, потому что я использовал тот же драйвер, Scala и Java в небольшом тестовом приложении. Все работало хорошо, как только я убрал логику объединения в этом файле build.sbt и вручную исключил дубликаты. К сожалению, в реальном приложении используется гораздо больше библиотек.

Есть ли способ гарантировать, что sbt-assembly сначала выбирает классы из jar-файла MariaDB Connector/J? Если нет, есть ли у кого-то файл build.sbt, включающий этот драйвер?

Попробовал:

  1. Заменил библиотеку.
  2. Использовал разные версии MariaDB Connector/J.
  3. Подключился к MySQL 8, когда использую плагин аутентификации mysql_native_password.
  4. Подключился к серверу MySQL 5.7, но постоянно не удается подключиться к серверу MySQL 8 “из коробки”.

Ожидание: бесшовная замена драйвера MySQL на драйвер MariaDB.

ИЗМЕНИЛ: Вот пример приложения, которое работает. Чтобы его сломать, удалите раздел excludeDependencies и замените его на стандартную логику объединения.

Maria.scala

package com.test

import java.sql.Connection
import java.sql.DriverManager
import java.sql.SQLException
import org.mariadb.jdbc.Driver

object Maria {
  def main(args: Array[String]) = {
    var connection:Connection = null

    try {
      println("Начало...")
      Class.forName("org.mariadb.jdbc.Driver")
      println("Драйвер зарегистрирован")
      connection = DriverManager.getConnection(
        "jdbc:mariadb://maria-mysql2-1:3306/test?allowPublicKeyRetrieval=true", 
        "root", "password")
        
      println(s"Подключение корректно: ${connection.isValid(5)}")

      val statement = connection.createStatement()
      val resultSet = statement.executeQuery("SELECT id, name FROM test")
      while ( resultSet.next() ) {
        val id = resultSet.getInt("id")
        val name = resultSet.getString("name")
        println(s"> ${id}. ${name}")
      }
    } catch {
      case e: Exception => {
        println(e.printStackTrace)
      }
    } finally {
      println("Закрытие...")
      connection.close()
      println(s"Подключение корректно: ${connection.isValid(5)}")
    }
  }
}

build.sbt

version := "0.1.0-SNAPSHOT"
organization := "com.test"
scalaVersion := "2.12.19"
name := "Maria"

Compile / mainClass := Some("com.test.Maria")
assembly / mainClass := Some("com.test.Maria")
assembly / assemblyJarName := s"${name.value}-${version.value}.jar"
libraryDependencies ++= Seq(
  "org.scala-lang.modules" %% "scala-parser-combinators" % "2.3.0",
  "org.mariadb.jdbc" % "mariadb-java-client" % "2.7.12"
)

excludeDependencies ++= Seq(
  ExclusionRule("org.slf4j", "slf4j-api"),
  ExclusionRule("org.slf4j", "jcl-over-slf4j")
)

Ответ или решение

Проблема, с которой вы столкнулись, связана с конфликтами классов при использовании плагина sbt-assembly. Этот плагин отвечает за объединение зависимостей в один JAR файл, и когда в различных библиотеках есть дублирующиеся классы, логика объединения может заменить нужные классы на не нужные, что и вызывает ошибку подключения, связанную с аутентификацией.

Решение

Для того чтобы избежать конфликтов и гарантировать, что классы из JAR файла MariaDB Connector/J будут использоваться в первую очередь, вы можете воспользоваться следующими шагами:

  1. Настройте порядок объединения зависимостей – для этого вы можете использовать параметр assembly / mergeStrategy. Он позволяет указать, как действовать с конфликтующими классами.

  2. Обновите файл build.sbt следующим образом:

version := "0.1.0-SNAPSHOT"
organization := "com.test"
scalaVersion := "2.12.19"
name := "Maria"

Compile / mainClass := Some("com.test.Maria")
assembly / mainClass := Some("com.test.Maria")
assembly / assemblyJarName := s"${name.value}-${version.value}.jar"

libraryDependencies ++= Seq(
  "org.scala-lang.modules" %% "scala-parser-combinators" % "2.3.0",
  "org.mariadb.jdbc" % "mariadb-java-client" % "2.7.12"
)

excludeDependencies ++= Seq(
  ExclusionRule("org.slf4j", "slf4j-api"),
  ExclusionRule("org.slf4j", "jcl-over-slf4j")
)

// Добавляем mergeStrategy для правильной обработки классов
assembly / mergeStrategy := {
  case PathList("META-INF", xs @ _*) => MergeStrategy.discard // Удаляем дубликаты файлов META-INF
  case "reference.conf" => MergeStrategy.concat // Объединяем файлы конфигурации
  case x =>
    val isDuplicateClass = Seq("org.mariadb.jdbc.Driver").exists(x.contains)
    if (isDuplicateClass) MergeStrategy.first // Сохраняем класс из JAR с MariaDB драйвером
    else MergeStrategy.first
}

Объяснение настроек mergeStrategy

  • *PathList("META-INF", xs @ _) => MergeStrategy.discard**: Удаляет все дубликаты файлов в каталоге META-INF, так как они часто не нужны для работы приложения и могут вызвать проблемы.

  • case "reference.conf" => MergeStrategy.concat: Удаляет конфликты в файлах конфигурации reference.conf, объединяя их.

  • val isDuplicateClass = Seq("org.mariadb.jdbc.Driver").exists(x.contains): Этот участок кода проверяет, является ли текущий класс соответствующим классу драйвера MariaDB, и если это так, сохраняет его как первый.

Итоги

С помощью настройки mergeStrategy вы можете гарантировать, что нужный класс будет использоваться. При выполнении вышеуказанных шагов вы должны избежать проблем, связанных с конфликтом библиотек при использовании sbt-assembly, и успешно подключаться к MySQL 8 с помощью MariaDB Connector/J.

Если после этих изменений проблема не решится, рекомендую также проверить конфигурацию вашего подключаемого сервера MySQL, убедившись, что он настроен на использование поддерживаемого механизма аутентификации.

Оцените материал
Добавить комментарий

Капча загружается...