从Scala程序中查找Scala库的位置



我正在尝试使一个Scala程序生成另一个Scala程序。我设法从System.getProperty("java.home")获得java可执行文件,我从System.getProperty("java.class.path") (sbt-launcher.jar位置)获得了一些路径,并与ClassLoader一起获得了project/target/scala-2.11/classes目录。

但是,我仍然无法运行它。JVM抱怨无法找到Scala库的类:

Exception in thread "main" java.lang.NoClassDefFoundError: scala/concurrent/ExecutionContext
  at java.lang.Class.getDeclaredMethods0(Native Method)
  at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
  at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
  at java.lang.Class.getMethod0(Class.java:3018)
  at java.lang.Class.getMethod(Class.java:1784)
  at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: scala.concurrent.ExecutionContext
  at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
  at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 7 more

我正在寻找一种方法来添加这些文件到classpath,但我希望它是可移植的。我不寻找像在本地计算机上硬编码scala位置的解决方案,也不想使用其他环境变量和参数,而不是已经存在的。我也不想依赖SBT或激活器在用户环境中的存在。

由于父JVM进程可以使用它们,它们的位置必须存储在某个地方,我将感谢帮助找到那个位置。

为了成功地从另一个Scala应用程序中生成一个Scala应用程序,我必须修复我的代码中的几个问题:

<标题> 1。正确的main类:
object ChildApp extends App {
  println("success")
}

要确保ChildApp可以被Java运行,它必须是object。Scala没有static的概念,但是对象方法(和main方法)会被编译成静态方法。

<标题> 2。正确的类名:

ChildApp.getClass.getName返回ChildApp$时,它引用了一个对象(这样我们就可以传递一个静态方法类)。Java在命令行中需要$ -在其他作品中,我必须在将$传递到进程构建器之前删除它。

<标题> 3。完整的类路径

我没有找到System.getPropertiy("java.class.path"):

中所有使用的jar:
val pcp = System getPropertiy "java.class.path" split File.pathSeparator // sbt-launcher.jar only

我没有在SystemClassLoader中找到它们:

val scp = ClassLoader.getSystemClassLoader.asInstanceOf[URLClassLoader].getURLs.map(_.toString) // same as above

我发现编译文件从我的项目使用类的资源:

// format like jar:file:/(your-project/compiled.jar)!some/package/ChildApp.class
lazy val jarClassPathPattern  = "jar:(file:)?([^!]+)!.+".r
// format like file:/(your-project/compiled/some/package/ChildApp).class
lazy val fileClassPathPattern = "file:(.+).class".r
val jcp = jarClassPathPattern.findFirstMatchIn(pathToClass) map { matcher =>
  val jarDir = Paths get (matcher group 2) getParent()
  s"${jarDir}/*"
} toSet
val fcp = fileClassPathPattern.findFirstMatchIn(pathToClass) map { matcher =>
  val suffix   = "/" + clazz.getName
  val fullPath = matcher group 1
  fullPath substring (0, fullPath.length - suffix.length)
} toList

最后我找到了所有这些依赖项存储的位置:

// use App class' ClassLoader instead of system one
val lcp = ChildApp.getClass.getClassLoader.asInstanceOf[URLClassLoader].getURLs.map(_.toString)
<标题> 4。奖励- JVM参数和java位置
val jvmArgs   = ManagementFactory.getRuntimeMXBean.getInputArguments.toList
lazy val javaHome = System getProperty "java.home"
lazy val java = Seq(
  Paths.get(javaHome, "bin", "java"),
  Paths.get(javaHome, "bin", "java.exe")
) filter (Files exists _) head

那么你就有了ProcessBuilder/Process所需的一切:

val executable = java.toString
val arguments  = jvmArgs ++ List("-cp", classPath, mainName) ++ mainClassArguments

p。我检查了几次-那些附加的 jar没有使用CLASSPATH环境变量也没有使用-cp参数(sbt-launcher.jar的MANIFEST文件也没有任何东西)。因此,如果有人知道他们是如何通过的,以及为什么我的解决方案实际上有效,请解释。

相关内容

  • 没有找到相关文章

最新更新