CLOVER🍀

That was when it all began.

Infinispan Tree APIを使用して、sbt+Scalaでビルドできない件について

最初に書いておきます、未解決です。
Unsolved!

(6/19 追記)
コンパイル、通せるようになりました!

http://d.hatena.ne.jp/Kazuhira/20130619/1371647811

この前、InfinispanのTree APIを使おうとして、sbt+Scalaでビルドに失敗するということを書きましたが、Twitterでもちょこっとつぶやいていました。

が、これを目に留めた方がいたようで(Infinispanの中の方??)、こんなツッコミをもらいました…。

それが昨日の深夜の話でして、JIRAで説明してと言われましても、JIRAがちょっとよくわからないし、説明できる自信ないし、そもそも時間も遅すぎたので(すげー言い訳)、お断りしたところ

デバッグしてみなよ的なことを言われたわけで…。

ちょっとだけ、頑張ってみることにしました。

ちなみに、前回はsbt+Scalaのビルドで失敗し、Gradle+Javaの組み合わせで成功しています。

準備

まずは、使う環境から。

Java
$ java -version
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)
sbt
> sbt-version
[info] 0.12.3
Gradle
$ gradle -version

------------------------------------------------------------
Gradle 1.6
------------------------------------------------------------

Gradle build time: 2013年5月7日 9時12分14秒 UTC
Groovy: 1.8.6
Ant: Apache Ant(TM) version 1.8.4 compiled on May 22 2012
Ivy: 2.2.0
JVM: 1.7.0_21 (Oracle Corporation 23.21-b01)
OS: Linux 3.8.0-25-generic amd64

実行のバリエーションをいくつか用意します。

sbt+Scala

build.sbt

scalaVersion := "2.10.2"

resolvers += "JBoss Public Maven Repository Group" at "http://repository.jboss.org/nexus/content/groups/public-jboss/"

libraryDependencies += "org.infinispan" % "infinispan-tree" % "5.3.0.CR2"

ムダに、Infinispan 5.3.0.CR2。まあ、他のバージョンを選んでも特に動きは変わりませんが…。

src/main/scala/InfinispanTreeExample.scala

import org.infinispan.manager.DefaultCacheManager
import org.infinispan.tree.{TreeCacheFactory, TreeCache}

object InfinispanTreeExample {
  def main(args: Array[String]): Unit = {
    val manager = new DefaultCacheManager
    val cache = manager.getCache[String, String]()

    val treeCache: TreeCache[String, String] = new TreeCacheFactory().createTreeCache(cache)
  }
}

コンパイル。

$ sbt
[info] Set current project to default-ea72c1 (in build file:/xxxxx/)
> compile
[info] Updating {file:/xxxxx/}default-ea72c1...
[info] Resolving org.jboss#staxmapper;1.1.0.Final ...
[info] Done updating.
[info] Compiling 1 Scala source to /xxxxx/target/scala-2.10/classes...
[warn] Class net.jcip.annotations.Immutable not found - continuing with a stub.
[warn] Caught: java.lang.NullPointerException while parsing annotations in /home/xxxxx/.ivy2/cache/org.infinispan/infinispan-tree/bundles/infinispan-tree-5.3.0.CR2.jar(org/infinispan/tree/Fqn.class)
[error] error while loading Fqn, class file '/home/xxxxx/.ivy2/cache/org.infinispan/infinispan-tree/bundles/infinispan-tree-5.3.0.CR2.jar(org/infinispan/tree/Fqn.class)' is broken
[error] (class java.lang.RuntimeException/bad constant pool index: 0 at pos: 8277)
[warn] two warnings found
[error] one error found
[error] (compile:compile) Compilation failed
[error] Total time: 9 s, completed 2013/06/18 23:06:13

やっぱり、コンパイルに失敗します。

原因は、
org.infinispan.tree.Fqn
というクラスがおかしい

[error] error while loading Fqn, class file '/home/xxxxx/.ivy2/cache/org.infinispan/infinispan-tree/bundles/infinispan-tree-5.3.0.CR2.jar(org/infinispan/tree/Fqn.class)' is broken
[error] (class java.lang.RuntimeException/bad constant pool index: 0 at pos: 8277)

って言ってますね。

sbt+Java

build.sbt

version := "0.0.1-SNAPSHOT"

resolvers += "JBoss Public Maven Repository Group" at "http://repository.jboss.org/nexus/content/groups/public-jboss/"

libraryDependencies += "org.infinispan" % "infinispan-tree" % "5.3.0.CR2"

src/main/java/InfinispanTreeExample.java

import org.infinispan.Cache;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.tree.TreeCache;
import org.infinispan.tree.TreeCacheFactory;

public class InfinispanTreeExample {
    public static void main(String[] args) throws Exception {
        DefaultCacheManager manager = new DefaultCacheManager();
        Cache<String, String> cache = manager.getCache();

        TreeCache<String, String> treeCache = new TreeCacheFactory().createTreeCache(cache);
    }
}
$ sbt
[info] Set current project to default-803641 (in build file:/xxxxx/)
> compile
[info] Updating {file:/xxxxx/}default-803641...
[info] Resolving org.jboss#staxmapper;1.1.0.Final ...
[info] Done updating.
[info] Compiling 1 Java source to xxxxx/target/scala-2.9.2/classes...
[success] Total time: 4 s, completed 2013/06/18 23:09:21

こちらは、コンパイルに成功しました。

Gradle+Scala

build.gradle

apply plugin: 'scala'

repositories {
    mavenCentral()

    maven {
        url 'https://repository.jboss.org/nexus/content/groups/public'
    }
}

dependencies {
    compile 'org.scala-lang:scala-library:2.10.2'
    compile 'org.infinispan:infinispan-tree:5.3.0.CR2'
}

src/main/scala/InfinispanTreeExample.scala

import org.infinispan.manager.DefaultCacheManager
import org.infinispan.tree.{TreeCacheFactory, TreeCache}

object InfinispanTreeExample {
  def main(args: Array[String]): Unit = {
    val manager = new DefaultCacheManager("infinispan.xml")
    val cache = manager.getCache[String, String]()

    val treeCache: TreeCache[String, String] = new TreeCacheFactory().createTreeCache(cache)
  }
}

コンパイル。

$ gradle compileScala
:compileJava UP-TO-DATE
:compileScala
[ant:scalac] warning: Class net.jcip.annotations.Immutable not found - continuing with a stub.
[ant:scalac] warning: Caught: java.lang.NullPointerException while parsing annotations in /home/xxxxx/.gradle/caches/artifacts-24/filestore/org.infinispan/infinispan-tree/5.3.0.CR2/bundle/aeb1b651609799b60df6a12e7805211fb2681606/infinispan-tree-5.3.0.CR2.jar(org/infinispan/tree/Fqn.class)
[ant:scalac] error: error while loading Fqn, class file '/home/xxxxx/.gradle/caches/artifacts-24/filestore/org.infinispan/infinispan-tree/5.3.0.CR2/bundle/aeb1b651609799b60df6a12e7805211fb2681606/infinispan-tree-5.3.0.CR2.jar(org/infinispan/tree/Fqn.class)' is broken
[ant:scalac] (class java.lang.RuntimeException/bad constant pool index: 0 at pos: 8277)
[ant:scalac] two warnings found
[ant:scalac] one error found
:compileScala FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileScala'.
> Compile failed with 1 error; see the compiler error output for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 17.244 secs

お?こっちも失敗した??

Gradle+Java

build.gradle

apply plugin: 'java'

repositories {
    mavenCentral()

    maven {
        url 'https://repository.jboss.org/nexus/content/groups/public'
    }
}

dependencies {
    compile 'org.infinispan:infinispan-tree:5.3.0.CR2'
}

src/main/java/InfinispanTreeExample.java

import org.infinispan.Cache;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.tree.TreeCache;
import org.infinispan.tree.TreeCacheFactory;

public class InfinispanTreeExample {
    public static void main(String[] args) throws Exception {
        DefaultCacheManager manager = new DefaultCacheManager();
        Cache<String, String> cache = manager.getCache();

        TreeCache<String, String> treeCache = new TreeCacheFactory().createTreeCache(cache);
    }
}

コンパイル。

$ gradle compileJava
:compileJava

BUILD SUCCESSFUL

Total time: 6.915 secs

まあ、こちらは普通に成功します。

ということは?

Scalaでコンパイルするといけない??

というわけで、sbtもGradleも何も使わずに、素のscalacで試してみることに。

Infinispanのダウンロードページより、5.3.0.CR2をダウンロードして、必要なJARファイルだけ取り出してきました。

http://www.jboss.org/infinispan/downloads

$ ll
合計 5620
drwxrwxr-x 3 xxxxx xxxxx   12288 Jun 18 23:16 ./
drwxrwxr-x 7 xxxxx xxxxx    4096 Jun 18 21:19 ../
-rw-rw-r-- 1 xxxxx xxxxx     374 Jun 18 21:19 InfinispanTreeExample.scala
-rw-r--r-- 1 xxxxx xxxxx 2661154 Jun 17 18:16 infinispan-core-5.3.0.CR2.jar
-rw-r--r-- 1 xxxxx xxxxx   67332 Jun 17 18:19 infinispan-tree.jar
-rw-rw-r-- 1 xxxxx xxxxx   60796 Mar 25 12:23 jboss-logging-3.1.1.GA.jar
-rw-rw-r-- 1 xxxxx xxxxx  229949 Mar 25 12:23 jboss-marshalling-1.3.15.GA.jar
-rw-rw-r-- 1 xxxxx xxxxx   82089 Mar 25 12:23 jboss-marshalling-river-1.3.15.GA.jar
-rw-rw-r-- 1 xxxxx xxxxx   11209 Mar 25 12:23 jboss-transaction-api_1.1_spec-1.0.0.Final.jar
-rw-rw-r-- 1 xxxxx xxxxx    2254 Mar 25 12:22 jcip-annotations-1.0.jar
-rw-r--r-- 1 xxxxx xxxxx 2079265 Jun  7 10:06 jgroups-3.3.1.Final.jar
-rw-rw-r-- 1 xxxxx xxxxx  481535 Mar 25 12:22 log4j-1.2.16.jar
-rw-rw-r-- 1 xxxxx xxxxx   36001 Mar 25 12:23 staxmapper-1.1.0.Final.jar

使うScalaコードは同じです。

では、コンパイル。

$ scalac -classpath "*.jar" InfinispanTreeExample.scala
$

予想に反して、コンパイルが通りましたが…。

もう意味分からんのですが…。ということは、sbtとかGradleを介しているのがNGということ??

Gradleでデバッグしてみる

いくつか試していて、GradleではscalacのAntタスクを使っていることがわかったので、scalacのコードを眺めつつ、先ほどの、Gradle+Scalaで使ったbuild.gradleに

compileScala {
  compileScala.scalaCompileOptions.additionalParameters= ['-Ydebug']
}

を加えて実行。

ものすごーくスタックトレースが流れますが、コケている直接の原因はここっぽいです。

[ant:scalac] java.lang.RuntimeException: bad constant pool index: 0 at pos: 8277
[ant:scalac] 	at scala.tools.nsc.symtab.classfile.ClassfileParser$ConstantPool.scala$tools$nsc$symtab$classfile$ClassfileParser$ConstantPool$$errorBadIndex(ClassfileParser.scala:406)
[ant:scalac] 	at scala.tools.nsc.symtab.classfile.ClassfileParser$ConstantPool.getName(ClassfileParser.scala:154)
[ant:scalac] 	at scala.tools.nsc.symtab.classfile.ClassfileParser.scala$tools$nsc$symtab$classfile$ClassfileParser$$parseAttribute$1(ClassfileParser.scala:849)
[ant:scalac] 	at scala.tools.nsc.symtab.classfile.ClassfileParser.parseAttributes(ClassfileParser.scala:1066)
[ant:scalac] 	at scala.tools.nsc.symtab.classfile.ClassfileParser.parseClass(ClassfileParser.scala:551)
[ant:scalac] 	at scala.tools.nsc.symtab.classfile.ClassfileParser.parse(ClassfileParser.scala:88)
[ant:scalac] 	at scala.tools.nsc.symtab.SymbolLoaders$ClassfileLoader.doComplete(SymbolLoaders.scala:261)
[ant:scalac] 	at scala.tools.nsc.symtab.SymbolLoaders$SymbolLoader.complete(SymbolLoaders.scala:194)
[ant:scalac] 	at scala.tools.nsc.symtab.SymbolLoaders$SymbolLoader.load(SymbolLoaders.scala:210)
[ant:scalac] 	at scala.reflect.internal.Symbols$Symbol.typeParams(Symbols.scala:1480)
[ant:scalac] 	at scala.reflect.internal.Types$NoArgsTypeRef.typeParams(Types.scala:2136)
[ant:scalac] 	at scala.reflect.internal.Types$NoArgsTypeRef.isHigherKinded(Types.scala:2135)
[ant:scalac] 	at scala.reflect.internal.transform.UnCurry$class.scala$reflect$internal$transform$UnCurry$$expandAlias(UnCurry.scala:13)
[ant:scalac] 	at scala.reflect.internal.transform.UnCurry$$anon$2.apply(UnCurry.scala:17)
[ant:scalac] 	at scala.reflect.internal.Types$TypeMap$$anonfun$noChangeToSymbols$1.apply(Types.scala:4272)
[ant:scalac] 	at scala.reflect.internal.Types$TypeMap$$anonfun$noChangeToSymbols$1.apply(Types.scala:4272)

Scala CompilerのClassfileParserで、コケていらっしゃる??

以下、 scala/tools/nsc/symtab/classfile/ClassfileParser.scala より。

ここのクラスを解析しているところで

  def parse(file: AbstractFile, root: Symbol): Unit = {
    debuglog("[class] >> " + root.fullName)

    pushBusy(root) {
      this.in           = new AbstractFileReader(file)
      this.clazz        = if (root.isModule) root.companionClass else root
      // WARNING! do no use clazz.companionModule to find staticModule.
      // In a situation where root can be defined, but its companionClass not,
      // this would give incorrect results (see SI-5031 in separate compilation scenario)
      this.staticModule = if (root.isModule) root else root.companionModule
      this.isScala      = false

      parseHeader
      this.pool = new ConstantPool
      parseClass()
    }
  }

追っていって、ここの最後のConstantPool#getName(index: Int)を呼び出し

  def parseAttributes(sym: Symbol, symtype: Type) {
    def convertTo(c: Constant, pt: Type): Constant = {
      if (pt.typeSymbol == definitions.BooleanClass && c.tag == IntTag)
        Constant(c.value != 0)
      else
        c convertTo pt
    }
    def parseAttribute() {
      val attrName = pool.getName(in.nextChar).toTypeName

その先で、indexの値が0ということでエラーケースに落ちているようです。

    /** Return the name found at given index. */
    def getName(index: Int): Name = {
      if (index <= 0 || len <= index)
        errorBadIndex(index)

最終的に、例外をスローしているのはここですね。

    /** Throws an exception signaling a bad constant index. */
    private def errorBadIndex(index: Int) =
      throw new RuntimeException("bad constant pool index: " + index + " at pos: " + in.bp)

クラスファイルの属性(Signature、InnerClasses、RuntimeVisibleAnnotationsとか)を解析している時に、その位置情報が不正だということですが…。

とまあ、コケている場所はわかったのですが、Fqnのクラスファイルのいったい何が気に入らないかまでは、結局わからないまま今日はタイムアップ。

いったん、ここまでかなぁ…。