Thursday, April 23, 2015

Running with alternate JVM args in sbt

It's pretty easy in sbt to run a main class in a forked JVM:

// Ensure we run outside of sbt. This is especially useful for setting JVM-level flags
fork in run := true

// Set flags for java. Memory, GC settings, properties, etc.
javaOptions ++= Seq("-Xmx4g")

mainClass in run := Some("mypackage.MyMainClass")

That lets you use "run" to spin up a forked JVM with your provided main class and arguments. (Aside: sbt-revolver is a MUCH friendlier way to run main classes).

But what if you want to run a main class with a different set of JVM flags? Maybe you have a memory hog, or you want to specify a debug logback config - or any other number of reasons. Or maybe you just want to have a shortcut for a long class name, or to have a default set of arguments for your main class.

This can be done with the Fork API:

// Create a task for your new main class. This is an input task so that you
// can provide arguments to it via the sbt console.
lazy val runMyOtherMain = inputKey[Unit]("run MyOtherMain")

runMyOtherMain := {
  // Parse the arguments typed on the sbt console.
  val args = sbt.complete.Parsers.spaceDelimited("[main args]").parsed
  // Build up the classpath for the subprocess. Yes, this must be done manually.
  // This also ensures that your code will be compiled before you run.
  val classpath = (fullClasspath in Compile).value
  val classpathString = Path.makeString(classpath map { _.data })
  // Any JVM args you want. You could use javaOptions.value to get your default
  // list, if you like.
  val jvmArgs = Seq("-Xmx4g", "-Dlogback.configurationFile=debug_logback.xml")
  Fork.java(
    // Full options include whatever you want to add, plus the classpath.
    ForkOptions(runJVMOptions = jvmArgs ++ Seq("-classpath", classpathString)),
    // You could also add other default arguments here.
    "mypackage.MyOtherMainClass" +: args
  )
}

Now, you can type runMyOtherMain arg1 arg2 from the sbt console, and it'll execute:

java -Xmx4g -Dlogback.configurationFile=debug_logback.xml -classpath [actual classpath] mypackage.MyOtherMainClass arg1 arg2

Sweet!

Monday, February 23, 2015

Partially applied functions and implicit parameters

In trying to test an akka Actor talking over TCP (a whole difficult subject of its own), I wanted to inject a factory method for the TCP actor ref into my Actor. Specifically, I wanted to inject IO.apply. The signature looks like:

// Used like "IO(Tcp)", where Tcp is the object identifying the TCP IO extension.
def apply[T <: Extension](key: ExtensionId[T])(implicit system: ActorSystem): ActorRef

Ignoring the awkward type parameter, this looks like a method with two parameter lists - one normal, and one implicit. My thought was that we could just bind the explicit list, and let the implicit list get bound later. Note that I couldn't figure out how to create a partially-applied function that was 0-ary (that is, all parameters were bound), so I had to have some weird typing:

import akka.actor.{ Actor, ActorRef, ActorSystem, Props }
import akka.io.{ IO, Tcp, TcpExt}
// Note that I hard-coded the type to Tcp for readability.
class MyActor(tcpFactory: Tcp.type => ActorRef) extends Actor {
  // To use:
  tcpFactory(Tcp) ! Tcp.Connect(hostname)
}
object MyActor {
  // Factory method for the class, used outside of unit tests.
  // WARNING: Can you spot the error here?
  def props()(implicit actorSystem: ActorSystem): Props = {
    // Constrain the type on the apply method, and partially bind it.
    Props(new MyActor(IO.apply[TcpExt] _))
  }
}

After this, I compiled & tested the code - it worked! But I was left with an unsettled feeling about the implicit, and thinking for a moment, it struck me: This potentially runs the factory method (IO.apply) in a different ActorSystem than the main actor itself! The reason being, of course, that the implicit is bound up in the partial method when you first call props(), not when it's later called within the Actor. This should be obvious by the partial's signature - Type.type => ActorRef doesn't have an ActorSystem anywhere in it!

The solution would be to bind both the implicit and explicit list to the partial - but I couldn't find any documentation on how to bind an implicit list into a partially-applied function, or even to declare it as such. From what I can tell, this is impossible. You can curry functions with multiple regular parameter lists, but you're out of luck for implicits. However, you can bind a partial function with a single argument list with the implicits tacked on:

class MyActor(tcpFactory: (Tcp.type, ActorSystem) => ActorRef) extends Actor {
  // Use 'implicitly' to get the value from the implicit scope.
  tcpFactory(Tcp, implicitly[ActorSystem]) ! Tcp.Connect(hostname)
}
object MyActor {
  // No more implicit parameters on the factory method!
  def props: Props = {
    // Looks like currying, confusingly.
    Props(new MyActor(_: Tcp.type)(_: ActorSystem))
  }
}

Cleaning up a bit more:

// Partially bind the Tcp parameter now.
class MyActor(tcpFactory: ActorSystem => ActorRef) extends Actor {
  tcpFactory(implicitly[ActorSystem]) ! Tcp.Connect(hostname)
}
object MyActor {
  def props: Props = Props(new MyActor(Tcp)(_: ActorSystem))
}