Загрузчик классов Scala для метода запуска Avro Tools, написанный на Java - PullRequest
0 голосов
/ 30 сентября 2019

У меня возникли трудности с определением способа загрузки класса "Avro Tools" и его метода run. Проблема где-то между интерфейсами Java и Scala и методами загрузки классов. В связи с тем, что avro используется в другом месте в приложении Spark с другой версией для загрузки файлов данных, мне нужно иметь возможность рассматривать этот конкретный метод как отдельный вызов другой версии avro-tools.

Ниже приведен мой код:

package samples

import java.io.{ByteArrayOutputStream, InputStream}
import org.junit.runner.RunWith
import org.specs2.mutable._
import org.specs2.runner._

import scala.collection.JavaConverters._

@RunWith(classOf[JUnitRunner])
class MySpecTest extends Specification {
  "Class Loader" should {
    "load an implement a class" in {

      var classLoader = new java.net.URLClassLoader(
        Array(new java.io.File("./avro-tools-1.9.1.jar").toURI.toURL),
        this.getClass.getClassLoader)

      var clazzDFRT = classLoader.loadClass("org.apache.avro.tool.DataFileRepairTool")

      val objDFRT = clazzDFRT.getConstructor().newInstance()
      val toolCmdArgsAsJava = List("-o", "all", "questionable.avro", "fixed.avro").asJava
      val stdin : InputStream = null
      val out: ByteArrayOutputStream = new ByteArrayOutputStream
      val stdout = new PrintStream(out) // added stdout in edit#1

      val err = System.err
      val toolClassArgsAsJava = List(stdin, stdout, // changed out to stdout in edit#1 
          err, toolCmdArgsAsJava).asJava 

      //  parameterTypes: Class[_] *
      //  public int run( InputStream stdin, PrintStream out, PrintStream err, List<String> args)

      val paramClasses: Array[Class[_]] = Array(classOf[InputStream], classOf[PrintStream], classOf[PrintStream], classOf[java.util.List[_]])

      val method = clazzDFRT.getMethod("run",  paramClasses : _*)

      // the following produces wrong number of arguments exception
      method.invoke(objDFRT.asInstanceOf[Object], toolClassArgsAsJava)

      // sidebar: is this the end result for the Unit test - want out str with summary
      out.toString("UTF-8").contains("File Summary")
    }
  }
}

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

Как я могу это исправить, чтобы запустить весь сегмент кода (и восстановить поврежденный avro)?

1 Ответ

0 голосов
/ 30 сентября 2019

Трудно сказать точную природу проблемы, поскольку вы не включили исключение или трассировку стека. Я не уверен, почему вы загружаете инструменты avro динамически, а не включаете jar статически как часть вашей сборки.

//  public int run( InputStream stdin, PrintStream out, PrintStream err, List<String> args)
val method = clazzDFRT.getMethod("run",  Class[_] : _*)

Вы не указали параметры правильно.

method.invoke(objDFRT.asInstanceOf [Object], toolClassArgsAsJava)

val params: Array[Class[_]] = Array(classOf[InputStream], classOf[PrintStream], classOf[PrintStream], classOf[java.util.List[_]])
val method = clazzDFRT.getMethod("run",  params : _*)

или

val method = clazzDFRT.getMethod("run", classOf[InputStream], classOf[PrintStream], classOf[PrintStream], classOf[java.util.List[_]])

Чтобы исправить вызов, вы не можете передать параметры в списке. Метод invoke принимает переменные аргументы, вам нужно передать их напрямую.

method.invoke(objDFRT.asInstanceOf[Object], stdin, stdout, stderr, toolCmdArgsAsJava)

или

method.invoke(objDFRT.asInstanceOf[Object], Array(stdin, stdout, stderr, toolCmdArgsAsJava): _*)

Обратите внимание, что во втором варианте используется Array, а не List.

Предлагаю вам ознакомиться с документацией по использованию аргументов var в Java и Scala * https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html * http://daily -scala.blogspot.com / 2009/11 / varargs.html

...