New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

classLoader related problem in Multi-stage scripts with @main #591

Closed
scalway opened this Issue Apr 22, 2017 · 1 comment

Comments

Projects
None yet
2 participants
@scalway
Contributor

scalway commented Apr 22, 2017

this code:

#!/usr/bin/env amm11
interp.load.ivy("com.typesafe.akka" %% "akka-remote"  % "2.4.17")
@
import scala.concurrent.duration.DurationInt
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.{ConfigFactory, ConfigValueFactory}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.io.StdIn
import scala.util.{Failure, Success}

class A(portOut:Int) extends Actor {
  val select = s"akka.tcp://main$portOut@localhost:$portOut/user/a"
  val remote = context.actorSelection(select)
  remote.resolveOne(1.second).onComplete{
    case Success(a) => a ! "hey"
    case Failure(err) => print("error." + err)
  }
  def receive = { case s => println("\nMSG ----------------------------\n" +  s.toString + "\n") }
}

@main def server(): Unit = startApp(65100, 65200)
@main def client(): Unit = startApp(65200, 65100)

def startApp(portMe:Int, portOut:Int) = {
  val configstr = s"""
    |akka {
    |  actor {
    |    provider = remote
    |  }
    |  remote {
    |    enabled-transports = ["akka.remote.netty.tcp"]
    |    netty.tcp {
    |      hostname = localhost
    |      port = $portMe
    |    }
    |  }
    |}
    |""".stripMargin

  implicit val as = ActorSystem("main"+portMe, ConfigFactory.parseString(configstr))

  as.actorOf(Props(classOf[A], portOut), "a")
  StdIn.readLine()
}

throws such runtime exception:

Exception in thread "main" com.typesafe.config.ConfigException$Missing: No configuration setting found for key 'akka.version'

and this works fine:

#!/usr/bin/env amm11
interp.load.ivy("com.typesafe.akka" %% "akka-remote"  % "2.4.17")
@
import scala.concurrent.duration.DurationInt
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.{ConfigFactory, ConfigValueFactory}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.io.StdIn
import scala.util.{Failure, Success}

class A(portOut:Int) extends Actor {
  val select = s"akka.tcp://main$portOut@localhost:$portOut/user/a"
  val remote = context.actorSelection(select)
  remote.resolveOne(1.second).onComplete{
    case Success(a) => a ! "hey"
    case Failure(err) => print("error." + err)
  }
  def receive = { case s => println("\nMSG ----------------------------\n" +  s.toString + "\n") }
}

// -------------- changed --------------------------

def server(): Unit = startApp(65100, 65200) //<-- @main goes away :)
def client(): Unit = startApp(65200, 65100)  //<-- @main goes away :)

try {  // methods called directly
  server() 
} catch {
  case e => client()
}

// -------------- end of changed --------------------------

def startApp(portMe:Int, portOut:Int) = {
  val configstr = s"""
    |akka {
    |  actor {
    |    provider = remote
    |  }
    |  remote {
    |    enabled-transports = ["akka.remote.netty.tcp"]
    |    netty.tcp {
    |      hostname = localhost
    |      port = $portMe
    |    }
    |  }
    |}
    |""".stripMargin

  implicit val as = ActorSystem("main"+portMe, ConfigFactory.parseString(configstr))

  as.actorOf(Props(classOf[A], portOut), "a")
  StdIn.readLine()
}

it seams that there are different classLoaders used in both cases (in first code snipped akka cannot read reference.conf from .jar resources)

@lihaoyi

This comment has been minimized.

Owner

lihaoyi commented Apr 30, 2017

I suspect we may need to wrap this:

  • mainMethod.invoke(args, kwargs) match{
    case Router.Result.Success(x) => None
    case Router.Result.Error.Exception(x) => Some(Res.Exception(x, ""))
    case Router.Result.Error.TooManyArguments(x) =>
    Some(Res.Failure(
    None,
    Util.normalizeNewlines(
    s"""Too many args were passed to this script: ${x.map(literalize(_)).mkString(", ")}
    |expected arguments: $expectedMsg""".stripMargin
    )
    ))
    case Router.Result.Error.RedundantArguments(x) =>
    Some(Res.Failure(
    None,
    Util.normalizeNewlines(
    s"""Redundant values were passed for arguments: ${x.map(literalize(_)).mkString(", ")}
    |expected arguments: $expectedMsg""".stripMargin
    )
    ))
    case Router.Result.Error.InvalidArguments(x) =>
    Some(Res.Failure(
    None,
    "The following arguments failed to be parsed:" + Util.newLine +
    x.map{
    case Router.Result.ParamError.Missing(p) =>
    s"(${renderArg(p)}) was missing"
    case Router.Result.ParamError.Invalid(p, v, ex) =>
    s"(${renderArg(p)}) failed to parse input ${literalize(v)} with $ex"
    case Router.Result.ParamError.DefaultFailed(p, ex) =>
    s"(${renderArg(p)})'s default value failed to evaluate with $ex"
    }.mkString(Util.newLine) + Util.newLine + s"expected arguments: $expectedMsg"
    ))
    }

With this:

in order to properly set up the classloader. @scalway do you want to try that, and if it works send a PR?

@lihaoyi lihaoyi closed this in 81e0d13 May 12, 2017

jhnsmth added a commit to jhnsmth/Ammonite that referenced this issue May 21, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment