2015-08-07 6 views
0

Я пытаюсь создать одну программу Scala, которая запускает еще одну программу Scala. Мне удалось получить java исполняемый файл от System.getProperty("java.home"), я получил какой-то путь от System.getProperty("java.class.path") (sbt-launcher.jar), а также с ClassLoader У меня есть project/target/scala-2.11/classes.Поиск местоположения библиотек Scala из программы Scala

Однако я все еще не могу запустить его. 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 

Я ищу способ, чтобы добавить эти файлы в путь к классам, но я хочу, чтобы быть портативными. Я не ищу решения, такие как расположение жесткого кодирования scala на локальном компьютере, и я не хочу использовать другие переменные среды и параметры, чем уже существующие. Я также не хочу полагаться на присутствие SBT или Activators в среде пользователя.

Поскольку процесс родительского JVM может использовать их, их местоположение должно быть где-то сохранено, и я буду благодарен за помощь в выяснении этого местоположения.

ответ

1

Чтобы успешно нерестится один Scala App от другого мне пришлось исправить несколько проблем с моим кодом:

1. правильно main класс:

object ChildApp extends App { 
    println("success") 
} 

, чтобы убедиться, что ChildApp исполняемая на Java она имеет должно быть object. Scala не имеет понятия static, но методы объекта будут (и основные) будут скомпилированы в статический метод.

2. Правильное название класса:

В то время как ChildApp.getClass.getName возвращается ChildApp$, это относится к объекту (так, чтобы мы могли пройти иначе антистатическую метод только для класса вокруг). Java ожидает $ в командной строке - в других работах мне пришлось удалить хвост $ перед тем, как передать его в процесс.

3. Полный путь класса

Я не нашел все используемые JARs в System.getPropertiy("java.class.path"):

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 

PS.Я проверил несколько раз - те дополнительные JARs не передаются, не используя ни переменную окружения CLASSPATH, ни параметр -cp (файл МАНИФЕСТА sbt-launcher.jar не имел ничего). Поэтому, зная, как они передаются и почему мое решение действительно работает, объясните, пожалуйста.