【问题标题】:Scala compiler and JVM disagree about version of Akka is loadedScala 编译器和 JVM 不同意加载 Akka 的版本
【发布时间】:2015-06-15 04:12:27
【问题描述】:

编辑:为了澄清,就好像编译器和运行时不同意哪个版本的 Akka 在类路径上。除了这种情况,编译器看到新方法但运行时引发NoSuchMethodError,当我稍后尝试调用ActorContext.children 时,我得到了同样的错误(编译器看到它但JVM 引发NoSuchMethod) .这个问题可能比 Akka 更普遍。

我已经完成了mvn clean 并多次检查了我的 Scala REPL 版本。

原问题:

2.2.1 版(我正在使用的版本)的 Akka documentationAPI 表示带有构造函数参数的 Actor 应该像这样构建:

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"))

虽然这可以编译,但当您尝试运行它时会引发 java.lang.NoSuchMethodError: akka.actor.Props$.apply(Ljava/lang/Class;Lscala/collection/Seq;)Lakka/actor/Props; 异常。

在 REPL 上运行它会导致

<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"))
                                               ^

如果我接受 REPL 的建议更改,

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

它可以在没有评论 REPL 的情况下工作,并且有一个

[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"))
                               ^

编译器中的警告。文档和Migration Guide (2.1.x to 2.2.x) 都确认了这种弃用,称闭包技术(我正在使用的技术)导致不可序列化的Actor。

我不想使用已弃用的东西,尤其是因为我刚刚开始使用这个库。我不明白关于Props.withDispatcher 和朋友的评论,因为我只想使用默认调度程序,至少现在是这样。有没有这种工作的例子?

编辑:我的 pom.xml 如下所示。我已经注释掉了所有的测试,因为没有适用于 Scala 2.10.2 的 scalatest 版本。我已经多次清理了target 目录:在任何地方都没有任何其他 Scala 版本的提示(也从来没有任何其他版本的 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>

【问题讨论】:

    标签: scala constructor akka


    【解决方案1】:

    如果您使用 akka 2.2.x 和 scala 2.10.y,您可能会遇到问题:查看 scala/lib 目录,您会在其中看到 akka-actors.jar,这是 akka 的 2.1.z 版本(看看里面在 MANIFEST 或一些典型的类中)。因此,如果您像这样运行您的应用程序:

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

    2.1 jar 的 akka 将首先(自动)添加,并且不会被 2.2 从您的类路径覆盖。

    解决方法:

    在没有 scala 包装器的情况下手动运行它:

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

    附:我不明白为什么 akka jar 是随 scala 一起提供的

    【讨论】:

    • 确实:就是这样。为了测试它,我暂时将 akka-actors.jar 移出 $SCALA_HOME/lib 并在 Scala REPL 上运行我的测试。它工作得很好。我认为结论是,如果您打算使用 REPL 工作,Scala 2.10.2 与 Akka 2.2.x 不兼容。
    • 这为我们提供了更简单的解决方法:从 scala/lib 中删除 akka-actor.jar :)
    • 是的,但我不能指望我的包的用户这样做,虽然我希望他们能够使用 REPL,所以我只是将我的依赖降级到 Akka 2.1.4。 Scala 捆绑 Akka 的原因是他们希望将其吸收到标准库中,这是一个合理的目标,但它引入了一个难以调试的错误。
    • 我已经向 akka 邮件列表发布了一个问题:groups.google.com/forum/#!topic/akka-user/hC2Fn4d6TZc
    【解决方案2】:

    尝试查看类路径中的库。即使您指定了运行 scala -cp 的类路径,scala 也会自动将 scala 标准库和其他库添加到您的 scala 主目录的 lib 目录中。请查看here

    尝试在 main 中编写一段代码来打印类路径。它将帮助您找到正在加载的内容。这个one 是在java 中,但它可以提供帮助。

    我复制并粘贴了您的第一个示例,就像在 API 中指定的那样,并使用此 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"
        )
    }
    

    它就像一个魅力:

    $ 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
    

    【讨论】:

    • 不,即使我包含这些库,我的示例的行为方式也相同。 (例如,来自 REPL 的 scala -cp akka-actor_2.10-2.2.1.jar:akka-testkit_2.10-2.2.1.jar:akka-remote_2.10-2.2.1.jar 或编译到 Maven 项目中。)
    • 我认为您的类路径中首先出现了旧版本的 akka。尝试使用我的 build.sbt 并检查类路径中正在加载的内容。如果您的 pom.xml 引用依赖于 akka 2.1 的库,它将被加载。
    • pom 下载的唯一库是 Akka 2.2.1。当然,如果我在类路径上显式加载它们,就像我之前评论中的示例一样,那么我就不会得到错误的版本。 (如果我拼写错误,我可能会加载 no Akka,但我不会弄错。)
    • 我认为在您的澄清中提供有关您的编译和运行环境的详细信息可能会很好。这不是 akka 问题,而是类路径问题。提供您的 pom.xml、scala 版本以及您用于编译和运行的命令将很有帮助。
    • 我逐渐意识到这一点,谢谢。 Scala 版本当然是 2.10.2,并且 pom(对于评论来说太大了)在上述命令行测试中应该无关紧要。我刚刚完成将所有这些移动到Main 类,使用mvn exec:java 而不是scala 命令行运行它,它可以工作。我现在正试图重新提出我的问题;我很困惑scala -cp ... 可能与具有相同库的mvn exec:java 不同——它可能会被甚至不在我的磁盘或我的类路径中的 jar 所混淆。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-22
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2020-11-13
    • 1970-01-01
    • 2021-06-27
    相关资源
    最近更新 更多