How can I run DataNucleus Bytecode Enhancer from SBT?

◇◆丶佛笑我妖孽 提交于 2019-12-06 03:33:12

You can either use java.lang.ProcessBuilder or sbt.Fork.

See below a generic javaRunner you can add to your build.sbt which employs java.lang.ProcessBuilder.

See also a generic sbtRunner you can add to your build.sbt which employs sbt.Fork. Thanks to @dwijnand for providing insightful information for making sbtRunner work as expected.

def javaRunner(mainClass: String,
               args: Seq[String],
               classpath: Seq[File],
               cwd: File,
               javaHome: Option[File] = None,
               runJVMOptions: Seq[String] = Nil,
               envVars: Map[String, String] = Map.empty,
               connectInput: Boolean = false,
               outputStrategy: Option[OutputStrategy] = Some(StdoutOutput)): Seq[File] = {

  val java_ : String      = javaHome.fold("") { p => p.absolutePath + "/bin/" } + "java"
  val jvm_  : Seq[String] = runJVMOptions.map(p => p.toString)
  val cp_   : Seq[String] = classpath.map(p => p.absolutePath)
  val env_                = envVars.map({ case (k,v) => s"${k}=${v}" })
  val xcmd_ : Seq[String] = Seq(java_) ++ jvm_ ++ Seq("-cp", cp_.mkString(java.io.File.pathSeparator), mainClass) ++ args

  println("=============================================================")
  println(xcmd_.mkString(" "))
  println("=============================================================")
  println("")

  IO.createDirectory(cwd)

  import scala.collection.JavaConverters._
  val cmd = xcmd_.asJava

  val pb = new java.lang.ProcessBuilder(cmd)
  pb.directory(cwd)
  pb.inheritIO
  val process = pb.start()
  def cancel() = {
    println("Run canceled.")
    process.destroy()
    1
  }
  val errno = try process.waitFor catch { case e: InterruptedException => cancel() }
  if(errno==0) {
    if (args.contains("-v")) cwd.list.foreach(f => println(f))
    cwd.listFiles
  } else {
    throw new IllegalStateException(s"errno = ${errno}")
  }
}

def sbtRunner(mainClass: String,
           args: Seq[String],
           classpath: Seq[File],
           cwd: File,
           javaHome: Option[File] = None,
           runJVMOptions: Seq[String] = Nil,
           envVars: Map[String, String] = Map.empty,
           connectInput: Boolean = false,
           outputStrategy: Option[OutputStrategy] = Some(StdoutOutput)): Seq[File] = {

  val args_ = args.map(p => p.toString)
  val java_ = javaHome.fold("None") { p => p.absolutePath }
  val cp_   = classpath.map(p => p.absolutePath)
  val jvm_  = runJVMOptions.map(p => p.toString) ++ Seq("-cp", cp_.mkString(java.io.File.pathSeparator))
  val env_  = envVars.map({ case (k,v) => s"${k}=${v}" })

  def dump: String =
    s"""
       |mainClass=${mainClass}
       |args=${args_.mkString(" ")}
       |javaHome=${java_}
       |cwd=${cwd.absolutePath}
       |runJVMOptions=${jvm_.mkString(" ")}
       |classpath --------------------------------------------
       |${cp_.mkString("\n")}
       |envVars ----------------------------------------------
       |${env_.mkString("\n")}
    """.stripMargin

  def cmd: String =
    s"""java ${jvm_.mkString(" ")} ${mainClass} ${args_.mkString(" ")}"""

  println("=============================================================")
  println(dump)
  println("=============================================================")
  println(cmd)
  println("=============================================================")
  println("")

  IO.createDirectory(cwd)
  val options =
    ForkOptions(
      javaHome = javaHome,
      outputStrategy = outputStrategy,
      bootJars = Seq.empty,
      workingDirectory = Option(cwd),
      runJVMOptions = jvm_,
      connectInput = connectInput,
      envVars = envVars)
  val process = new Fork("java", Option(mainClass)).fork(options, args)
  def cancel() = {
    println("Run canceled.")
    process.destroy()
    1
  }
  val errno = try process.exitValue() catch { case e: InterruptedException => cancel() }
  if(errno==0) {
    if (args.contains("-v")) cwd.list.foreach(f => println(f))
    cwd.listFiles
  } else {
    throw new IllegalStateException(s"errno = ${errno}")
  }
}

Then you need to wire DataNucleus Enhancer as part of your build process. This is done via manipulateBytecode sub-task, as demonstrated below:

lazy val model =
  project.in(file("model"))
    // .settings(publishSettings:_*)
    .settings(librarySettings:_*)
    .settings(paranoidOptions:_*)
    .settings(otestFramework: _*)
    .settings(deps_tagging:_*)
    //-- .settings(deps_stream:_*)
    .settings(deps_database:_*)
    .settings(
      Seq(
        // This trick requires SBT 0.13.8
        manipulateBytecode in Compile := {
          val previous = (manipulateBytecode in Compile).value
          sbtRunner(  // javaRunner also works!
            mainClass = "javax.jdo.Enhancer",
            args =
              Seq(
                "-v",
                "-pu", "persistence-h2",
                "-d",  (classDirectory in Compile).value.absolutePath),
            classpath =
              (managedClasspath in Compile).value.files ++
                (unmanagedResourceDirectories in Compile).value :+
                (classDirectory in Compile).value,
            cwd = (classDirectory in Compile).value,
            javaHome = javaHome.value,
            envVars = (envVars in Compile).value
          )
          previous
        }
      ):_*)
    .dependsOn(util)

For a complete example, including a few JDO annotated persistence classes and some rudimentary test cases, please have a look at

http://github.com/frgomes/poc-scala-datanucleus

I think the issue is you're passing your dependency jars as boot jars not as the classpath.

From your poc project perhaps something like:

val jvm_ = runJVMOptions.map(p => p.toString) ++
  Seq("-cp", cp_ mkString java.io.File.pathSeparator)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!