Skip to content

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

@scalway

Description

@scalway

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions