Jim Pivarski
Jim Pivarski

Reputation: 5974

Scala compiler and JVM disagree about version of Akka is loaded

Edit: To clarify, it is as though the compiler and runtime disagree about which version of Akka is on the classpath. In addition to this situation, in which the compiler sees new methods but the runtime raises NoSuchMethodError, I get the same sort of error when I later try to call ActorContext.children (the compiler sees it but the JVM raises NoSuchMethod). This problem might be more general than Akka.

I've done mvn clean and checked my Scala REPL version many times.

Original question:

The Akka documentation and API for version 2.2.1 (the version I'm using) says that Actors with constructor arguments should be built like this:

import akka.actor.Actor
import akka.actor.Props
import akka.actor.ActorSystem

class MyActor(one: Int, two: Double, three: String) extends Actor {
  def receive = {
    case "test" => println("%d %g %s".format(one, two, three))
  }
}

val system = ActorSystem("MyActorSystem")
val actor = system.actorOf(Props(classOf[MyActor], 1, 2.0, "three"))

Although this compiles, it raises a java.lang.NoSuchMethodError: akka.actor.Props$.apply(Ljava/lang/Class;Lscala/collection/Seq;)Lakka/actor/Props; exception when you try to run it.

Running it on the REPL results in

<console>:12 error: type mismatch;
 found   : Class[MyActor](classOf[$MyActor])
 required: () => akka.actor.Actor
       val actor = system.actorOf(Props(classOf[MyActor], 1, 2.0, "three"))
                                               ^

If I take the REPL's suggested change,

val actor = system.actorOf(Props(() => new MyActor(1, 2.0, "three")))

it works without comment on the REPL and with a

[warn] test.scala:10 method apply in object Props is deprecated: use Props.withDispatcher and friends
    val actor = system.actorOf(Props(classOf[MyActor], 1, 2.0, "three"))
                               ^

warning in the compiler. The documentation and Migration Guide (2.1.x to 2.2.x) both affirm this deprecation, saying that the closure technique (the one I'm using) results in non-serializable Actors.

I don't want to use something deprecated, especially since I'm just starting work with this library. I don't understand the comment about Props.withDispatcher and friends, since I just want to use the default dispatcher, at least for now. Are there any examples of this working?

Edit: My pom.xml is shown below. I've commented out all of the tests because there's no version of scalatest that works with Scala 2.10.2. I've cleaned the target directory many times: there's no hint of other Scala versions anywhere (and there never has been any other versions of Akka).

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <name>plotsmanship</name>
  <!-- <description>TODO</description> -->
  <inceptionYear>2013</inceptionYear>

  <groupId>org.plotsmanship</groupId>
  <artifactId>plotsmanship</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <licenses>
    <license>
      <name>The Apache Software License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>

  <properties>
    <maven.compiler.source>1.6</maven.compiler.source>
    <maven.compiler.target>1.6</maven.compiler.target>
    <encoding>UTF-8</encoding>
    <scala.tools.version>2.10</scala.tools.version>
    <scala.version>2.10.2</scala.version>
  </properties>

  <dependencies>
    <!-- Real dependencies for the jar -->
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>${scala.version}</version>
    </dependency>

    <dependency>
      <groupId>org.mozilla</groupId>
      <artifactId>rhino</artifactId>
      <version>1.7R4</version>
    </dependency>

    <dependency>
      <groupId>org.apache.avro</groupId>
      <artifactId>avro</artifactId>
      <version>1.7.5</version>
    </dependency>

    <dependency>
      <groupId>org.codehaus.jackson</groupId>
      <artifactId>jackson-core-asl</artifactId>
      <version>1.9.13</version>
    </dependency>

    <dependency>
      <groupId>org.codehaus.jackson</groupId>
      <artifactId>jackson-mapper-asl</artifactId>
      <version>1.9.13</version>
    </dependency>

    <dependency>
      <groupId>com.typesafe.akka</groupId>
      <artifactId>akka-actor_2.10</artifactId>
      <version>2.2.1</version>
    </dependency>

    <dependency>
      <groupId>com.typesafe.akka</groupId>
      <artifactId>akka-remote_2.10</artifactId>
      <version>2.2.1</version>
    </dependency>

    <dependency>
      <groupId>com.typesafe.akka</groupId>
      <artifactId>akka-testkit_2.10</artifactId>
      <version>2.2.1</version>
    </dependency>

    <!-- Dependencies for testing only (resolves to junit-4.11.jar hamcrest-core-1.3.jar scalatest_2.10-2.0.M6-SNAP8.jar) -->
    <!-- <dependency> -->
    <!--   <groupId>junit</groupId> -->
    <!--   <artifactId>junit</artifactId> -->
    <!--   <version>4.11</version> -->
    <!--   <scope>test</scope> -->
    <!-- </dependency> -->

    <!-- <dependency> -->
    <!--   <groupId>org.scalatest</groupId> -->
    <!--   <artifactId>scalatest_${scala.tools.version}</artifactId> -->
    <!--   <version>2.0.M6-SNAP8</version> -->
    <!--   <scope>test</scope> -->
    <!-- </dependency> -->

  </dependencies>

  <build>
    <sourceDirectory>src/main/scala</sourceDirectory>
    <!-- <testSourceDirectory>src/test/scala</testSourceDirectory> -->
    <plugins>

      <plugin>
        <!-- see http://davidb.github.com/scala-maven-plugin -->
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>3.1.3</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <!-- <goal>testCompile</goal> -->
            </goals>
            <configuration>
              <args>
                <arg>-deprecation</arg>
                <arg>-feature</arg>
                <!-- <arg>-make:transitive</arg>   (is an unsupported option) -->
                <arg>-dependencyfile</arg>
                <arg>${project.build.directory}/.scala_dependencies</arg>
              </args>
              <recompileMode>incremental</recompileMode>
              <useZincServer>true</useZincServer>
            </configuration>
          </execution>
        </executions>
      </plugin>

      <!-- <plugin> -->
      <!--   <groupId>org.apache.maven.plugins</groupId> -->
      <!--   <artifactId>maven-surefire-plugin</artifactId> -->
      <!--   <version>2.13</version> -->
      <!--   <configuration> -->
      <!--     <useFile>false</useFile> -->
      <!--     <disableXmlReport>true</disableXmlReport> -->
      <!--     <includes> -->
      <!--       <include>**/*Test.*</include> -->
      <!--       <include>**/*Suite.*</include> -->
      <!--     </includes> -->
      <!--   </configuration> -->
      <!-- </plugin> -->

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.4</version>
        <configuration>
          <archive>
            <manifest>
              <mainClass>org.plotsmanship.Main</mainClass>
              <addClasspath>true</addClasspath>
              <classpathPrefix>./lib</classpathPrefix>
            </manifest>
          </archive>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>
                target/lib
              </outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>
</project>

Upvotes: 2

Views: 1971

Answers (2)

Vatel
Vatel

Reputation: 106

If you use akka 2.2.x and scala 2.10.y you may have a problem: look into scala/lib directory, you will see akka-actors.jar there, this is 2.1.z version of akka (just look inside in MANIFEST or some typical classes). So if you run your app like this:

scala -cp $YourLibs:akka-actors-2.2.x ...

2.1 jar of akka will be added first (automatically) and will not be overrided by 2.2 from your classpath.

Workaround:

run it manually without scala wrapper:

java -cp $ScalaHome/lib/scala-library.jar:$AkkaHome/$Akka_2.2_jars YourMainClass

P.S. I do not understand why akka jar is shipped with scala

Upvotes: 2

Vinicius Miana
Vinicius Miana

Reputation: 2067

Try to take a look at the libraries in your classpath. Even when you specify a classpath running scala -cp, scala automatically adds the scala standard library and other libraries in the lib directory of your scala home. Please check here.

Try writing a piece of code in your main to print the classpath. It will help you find what is being loaded. This one is in java, but it can help.

I copied and pasted your first example, just like specified in the API and built it with this build.sbt:

name := "My Project"
version := "1.0"
scalaVersion := "2.10.2"
resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
libraryDependencies ++= {
    Seq(
      "com.typesafe.akka" %% "akka-actor" % "2.2.1",
      "com.typesafe.akka" %% "akka-remote" % "2.2.1",
      "com.typesafe.akka" %% "akka-testkit" % "2.2.1"
    )
}

and it worked like a charm:

$ sbt clean compile run
[info] Loading global plugins from ~/.sbt/plugins
...........
[success] Total time: 4 s, completed Sep 13, 2013 6:03:05 PM
[info] Running MyActor 
1 2.00000 three

Upvotes: 1

Related Questions