在我的一个Scala测试中,使用ProcessBuilder
,我在单独的JVM中启动了3个Apache Spark流媒体应用程序。(两个或多个火花流应用程序无法在同一JVM中共存。(一种Spark应用程序处理数据并摄入Apache Kafka,其他人会读取该数据。此外,测试涉及写入NOSQL数据库。
使用ProcessBuilder
时,使用:
val classPath = System.getProperty("java.class.path")
在Intellij中运行测试的工作原理,但是在CI系统上,测试由SBT的测试任务调用。在后一种情况下的java.class.path
将仅是sbt.jar
,因此,孩子JVM再次以NoClassFoundException
退出,如预期的那样。: - (
我正在寻找一种使用测试实际使用的类路径从SBT测试中"跨度" JVM的方法。例如,如果在项目core
中调用了测试,则应将项目core
的类路径提供给SPARK应用程序启动的儿童JVM。不幸的是,我不知道如何检索SBT任务中的正确类路径 - 然后可以将其提供给Child JVM。
Tests.Setup
可以用来访问SBT中的类路径:
testOptions in Test += Tests.Setup { classLoader =>
// give Spark classpath via classLoader
}
例如,在我的计算机上Tests.Setup(classLoader => println(classLoader))
给出
> test
ClasspathFilter(
parent = URLClassLoader with NativeCopyLoader with RawResources(
urls = List(/home/mario/sandbox/sbt/so-classpath/target/scala-2.12/test-classes, /home/mario/sandbox/sbt/so-classpath/target/scala-2.12/classes, /home/mario/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.12.4.jar, /home/mario/.ivy2/cache/org.scalatest/scalatest_2.12/bundles/scalatest_2.12-3.0.5.jar, /home/mario/.ivy2/cache/org.scalactic/scalactic_2.12/bundles/scalactic_2.12-3.0.5.jar, /home/mario/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.12.4.jar, /home/mario/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12/bundles/scala-xml_2.12-1.0.6.jar),
parent = DualLoader(a = java.net.URLClassLoader@3fcb37f1, b = java.net.URLClassLoader@271053e1),
resourceMap = Set(app.class.path, boot.class.path),
nativeTemp = /tmp/sbt_741bc913/sbt_c770779a
)
root = sun.misc.Launcher$AppClassLoader@33909752
cp = Set(/home/mario/.ivy2/cache/jline/jline/jars/jline-2.14.5.jar, /home/mario/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.12.4.jar, /home/mario/.ivy2/cache/org.scala-lang/scala-compiler/jars/scala-compiler-2.12.4.jar, /home/mario/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12/bundles/scala-xml_2.12-1.0.6.jar, /home/mario/sandbox/sbt/so-classpath/target/scala-2.12/classes, /home/mario/.ivy2/cache/org.scalatest/scalatest_2.12/bundles/scalatest_2.12-3.0.5.jar, /home/mario/.sbt/boot/scala-2.10.7/org.scala-sbt/sbt/0.13.17/test-interface-1.0.jar, /home/mario/.ivy2/cache/org.scalactic/scalactic_2.12/bundles/scalactic_2.12-3.0.5.jar, /home/mario/sandbox/sbt/so-classpath/target/scala-2.12/test-classes, /home/mario/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.12.4.jar)
)
我们看到的
.../target/scala-2.12/test-classes
.../target/scala-2.12/classes
存在。
另一方面,要从测试本身中检索类路径:
val classLoader = this.getClass.getClassLoader
// give Spark classpath via classLoader
例如,在我的机器上,println(classLoader)
在以下测试中给出
class CubeCalculatorTest extends FunSuite {
test("CubeCalculator.cube") {
val classLoader = this.getClass.getClassLoader
println(classLoader)
assert(CubeCalculator.cube(3) === 27)
}
}
打印
URLClassLoader with NativeCopyLoader with RawResources(
urls = List(/home/mario/sandbox/sbt/so-classpath/target/scala-2.12/test-classes, /home/mario/sandbox/sbt/so-classpath/target/scala-2.12/classes, /home/mario/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.12.4.jar, /home/mario/.ivy2/cache/org.scalatest/scalatest_2.12/bundles/scalatest_2.12-3.0.5.jar, /home/mario/.ivy2/cache/org.scalactic/scalactic_2.12/bundles/scalactic_2.12-3.0.5.jar, /home/mario/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.12.4.jar, /home/mario/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12/bundles/scala-xml_2.12-1.0.6.jar),
parent = DualLoader(a = java.net.URLClassLoader@6307eb76, b = java.net.URLClassLoader@271053e1),
resourceMap = Set(app.class.path, boot.class.path),
nativeTemp = /tmp/sbt_fa64d1a1/sbt_66bd50e2
)
我们再次看到
.../target/scala-2.12/test-classes
.../target/scala-2.12/classes
存在。
在测试中实际将类路径传递给ProcessBuilder
:
import java.net.URLClassLoader
import sys.process._
class CubeCalculatorTest extends FunSuite {
test("CubeCalculator.cube") {
val classLoader = this.getClass.getClassLoader
val classpath = classLoader.asInstanceOf[URLClassLoader].getURLs.map(_.getFile).mkString(":")
s"java -classpath $classpath MyExternalApp".!
...
}
}
您可以在ProcessBuilder
的JVM中获得完整的classpath
,从sbt
测试中使用:
Thread
.currentThread
.getContextClassLoader
.getParent
.asInstanceOf[java.net.URLClassLoader]
.getURLs
.map(_.getFile)
.mkString(System.getProperty("path.separator"))