views:

194

answers:

1

I already handled to start another VM in Java. See ProcessBuilder - Start another process / JVM - HowTo?

For some reason, I can't manage to do the same in Scala. Here's my code

object NewProcTest {
  def main(args :Array[String]) {
    println("Main")
//    val clazz = classOf[O3]
    val clazz = O4.getClass
    Proc.spawn(clazz, true)
    println("fin")
  }
}

object Proc{
  def spawn(clazz :Class[_],  redirectStream :Boolean)  {
    val separator = System.getProperty("file.separator")
    val classpath = System.getProperty("java.class.path")
    val path = System.getProperty("java.home") + 
      separator + "bin" + separator + "java"
    val processBuilder = 
            new ProcessBuilder(path, "-cp", 
            classpath, 
            clazz.getCanonicalName())
    processBuilder.redirectErrorStream(redirectStream)
    val process = processBuilder.start()
    process.waitFor()
    System.out.println("Fin")
  }
}

I've tried to define the main in an object and in class. Both within the same .scala file or within a separate one.

What am I doing wrong?

+2  A: 

The issue seems to be that the class name for an object has a '$' suffix.

If you strip off that suffix, the Java invocation line triggered from ProcessBuilder works.

I've hacked something below to show a couple of test cases. I'm not yet sure yet why this is the case but at least it provides a workaround.

import java.io.{InputStreamReader, BufferedReader}
import System.{getProperty => Prop}

object O3 {def main(args: Array[String]) {println("hello from O3")}}

package package1 {
  object O4 {def main(args: Array[String]) {println("hello from O4")}}
}

object NewProcTest {
  val className1 = O3.getClass().getCanonicalName().dropRight(1)
  val className2 = package1.O4.getClass().getCanonicalName().dropRight(1)
  val sep        = Prop("file.separator")
  val classpath  = Prop("java.class.path")
  val path       = Prop("java.home")+sep+"bin"+sep+"java"

  println("className1 = " + className1)
  println("className2 = " + className2)

  def spawn(className:      String,
            redirectStream: Boolean) {
    val processBuilder = new ProcessBuilder(path, "-cp", classpath, className)
    val pbcmd          = processBuilder.command().toString()

    println("processBuilder = " + pbcmd)

    processBuilder.redirectErrorStream(redirectStream)

    val process = processBuilder.start()
    val reader  = new BufferedReader(new InputStreamReader(process.getInputStream()))

    println(reader.readLine())
    reader.close()
    process.waitFor()
  }

  def main(args :Array[String]) {
    println("start")
    spawn(className1, false)
    spawn(className2, false)
    println("end")
  }
}
Don Mackenzie
This sounds reasonable, but it doesn't work for me. Copying your code in the default package of my Eclipse ScalaTest project results in:className1 = O3className2 = package1.O4startprocessBuilder = [Z:\Coden\java\jre6x64\bin\java, -cp, Z:\Coden\WS-Scala-2.8\ScalaTest\bin, O3]
Stefan K.
@Stefan K, I tested the above as a script on IntelliJ 9.0.2 and got "hello from O3" and "hello from O4". Both waitFor calls returned 0. For a run with the $ suffix restored in the class name I get null, null from the streams and the waitFor calls return 1, 1. It could be an environmental issue, perhaps you could add "echo" as the first parameter to the ProcessBuilder constructor call and see what's output and possibly copy and paste into a shell?
Don Mackenzie
With java, there's no problem with eclipse, that's confusing me. Here's the output of all constructor parameters of ProcessBuilder:Path: Z:\Coden\java\jre6x64\bin\javaClassP: Z:\Coden\WS-Scala-2.8\ScalaTest\binclassName: O3Thanks for your patience.
Stefan K.
The classpath entry above looks nonstandard are you sure the scala-library.jar is in that directory?
Don Mackenzie