9

I am trying to run something I have been successfully running under a variety of conditions for months. I am using akka-actor_2.11 2.3.4 with scala-library 2.11.7 from a Java application running Java 7. Like I said, the same code has worked for months. Under the most recent circumstances, I am getting the following:

java.lang.ClassCastException: interface akka.actor.Scheduler is not assignable from class akka.actor.LightArrayRevolverScheduler
at akka.actor.ReflectiveDynamicAccess$$anonfun$getClassFor$1.apply(DynamicAccess.scala:69)
at akka.actor.ReflectiveDynamicAccess$$anonfun$getClassFor$1.apply(DynamicAccess.scala:66)
at scala.util.Try$.apply(Try.scala:192)
at akka.actor.ReflectiveDynamicAccess.getClassFor(DynamicAccess.scala:66)
at akka.actor.ReflectiveDynamicAccess.createInstanceFor(DynamicAccess.scala:84)
at akka.actor.ActorSystemImpl.createScheduler(ActorSystem.scala:677)
at akka.actor.ActorSystemImpl.<init>(ActorSystem.scala:576)
at akka.actor.ActorSystem$.apply(ActorSystem.scala:142)
at akka.actor.ActorSystem$.apply(ActorSystem.scala:109)
at akka.actor.ActorSystem$.create(ActorSystem.scala:57)
at akka.actor.ActorSystem.create(ActorSystem.scala)

The call in question is: system= ActorSystem.create("MyActorSystem");

This call happens in the constructor for a class, which is loaded dynamically, via reflection ala getConstructor(...).newInstance(...).

The issue has arisen when a user of the distributed computation environment I wrote that this is based on tries to execute a job that dynamically instantiates a job that dynamically instantiates a job (2 levels of reflective instantiation). Basically, something like this pseudo call list :

reflectivelyCreate(job)
job.reflectivelyCreate(job2)
job2.instantiateActorSystem()
<fail>

If I run the exact same code in the following manner:

reflectivelyCreate(job2)
job2.instantiateActorSystem()
<success>

Everything works.

My question is whether there is some stateful class loader-related magic happening behind-the-scenes in Java such that Akka's assumptions about its state are incorrect. Similar issues I found (Akka ActorSystem creation issue) seemed to have to do with threading / scala repl invocation, but my code is single-threaded, and invoked the same way in both the success and failure cases above. The problematic invocation occurs only with a difference in the depth of the call stack as far as I can tell so far.

I would appreciate any input from akka gurus!

Community
  • 1
  • 1
mephicide
  • 314
  • 2
  • 11
  • 2
    Supposing that class `akka.actor.LightArrayRevolverScheduler` does, in fact, implement interface `akka.actor.Scheduler`, the only possible explanations for the exception presented in your stack trace revolve around funny business with `ClassLoader`s. I'm afraid I find Akka's documentation rather lacking in relevant detail, however, so I have no solution to offer. – John Bollinger Mar 29 '16 at 17:30
  • I am able to work around the problem with a Singleton pattern usage encapsulating the relevant ActorSystem components, which I can initialize at the start of my java process and get as-needed. I just never had to do this until this odd problem cropped up, and maybe someone else will have a similar issue and perhaps benefit from an explanation from someone in-the-know. – mephicide Mar 29 '16 at 18:16
  • Maybe you're having dependency version problems. What do you use for building? If you use maven and a pom, what happens if you pin the versions using ...? – Jeroen van Dijk-Jun Nov 08 '16 at 12:43

1 Answers1

1

If anyone else stumbles across this; the problem in my case was that I was using a scala interpreter within the same process that might also make use of Akka. Other multiple-classloader scenarios may exhibit the same issue. Here is the fix for my case:

  • Save the classloader I like (the one that loaded akka libraries) in a static field upon program initialization with private static ClassLoader ourClassLoader = CmdLineMain.class.getClassLoader();
  • After using the scala interpreter (which apparently replaces the classloader with one it likes), call Thread.currentThread().setContextClassLoader(ourClassLoader);

My program is single-threaded, but there are certainly other ways to solve the problem such that each thread maintains its own loader. That is beyond the scope of this post ;)

mephicide
  • 314
  • 2
  • 11