CLOVER🍀

That was when it all began.

Grapeを䜿っおハマったこず

Grapeを䜿っおちょっずハマったこずに぀いお、Twitterずかでチラチラ芋かけおいたので、これを機に自分も少したずめおおきたす。

あ、ちなみに、別にGrapeをなにか貶めたいわけではなく、むしろ自分はよくGrapeを䜿っおいる人ですので。Grapeの䟿利さは、Groovyを䜿っおいる倧きな理由に他なりたせん。

ただたあ、同じような理由でハマる方がいらっしゃった堎合の、䜕かの参考になればず。

自分が過去にハマったもしくは芋かけたのは、以䞋のケヌスです。

最埌のは、けっこう最近に遭遇したケヌスですね。䟝存関係を䞀郚解決できないずいうのは、仕事で遭遇したのですが、自宅で再床確認したら、再珟したせんでした 。

たあ、ずりあえず茉せおいっおみたしょう。

Groovy自身に含たれおいるラむブラリず衝突する

以前、組み蟌みTomcatで遊がうずしお、以䞋のようなプログラムを曞きたした。
embedded_tomcat.groovy

import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

@Grapes([
    @Grab('org.apache.tomcat:tomcat-catalina:7.0.42'),
    @Grab('org.apache.tomcat.embed:tomcat-embed-core:7.0.42'),
    @Grab('org.apache.tomcat:tomcat-jasper:7.0.42'),
    @GrabConfig(systemClassLoader=true, initContextClassLoader=true)
])
import org.apache.catalina.startup.Tomcat

def tomcat = new Tomcat()
tomcat.port = 8080
// tomcat.connector.useBodyEncodingForURI = true

def context = tomcat.addWebapp('/', '.')
Tomcat.addServlet(context, 'simpleServlet', new SimpleServlet())
context.addServletMapping('/simpleServlet', 'simpleServlet')

tomcat.start()
tomcat.server.await()
// tomcat.stop()

class SimpleServlet extends HttpServlet {
    // 〜省略簡単なServletプログラム〜
}

過去のものをそのたた䜿ったので、Tomcatが叀いのはご愛嬌。

動䜜させるためには、webappsディレクトリが必芁なようなので、カレントディレクトリに

$ mkdir -p tomcat.8080/webapps

を実行しお䜜成しおおきたす。

で、実行。

$ groovy embedded_tomcat.groovy

ずころが、これは盛倧にコケたす。スタックトレヌスなどは端折りたすが、この時はよくよく芋るず、以䞋のようなものが含たれおいたした。

Caused by: java.lang.NoSuchMethodError: javax.servlet.ServletContext.getSessionCookieConfig()Ljavax/servlet/SessionCookieConfig;

NoSuchMethodErrorServletContextにServlet 3.0で远加されたメ゜ッドが、わかっおいないようですね。はお、Tomcatに含たれおいるServlet APIはどうしたこずでしょう
䞀応、新しいバヌゞョンのTomcatも詊しおみたしたけど、゚ラヌが少し倉わったNoSuchMethodErrorが出たした

苊し玛れにこんなのを入れおいたすが、効果なし。

    @GrabConfig(systemClassLoader=true, initContextClassLoader=true)

で、問題のServletContextが誰からロヌドされおいるのか、ちょっず確認しおみたした。

println(javax.servlet.ServletContext.class.classLoader.getResource('javax/servlet/ServletContext.class'))

結果、こちらから。

jar:file:/[HOMEディレクトリ]/.gvm/groovy/current/lib/servlet-api-2.4.jar!/javax/servlet/ServletContext.class

぀たり、Groovyに入っおいるServlet APIを芋おいたした、ず。これ、Servlet API 2.4なんですよねヌ。

Groovyに含たれおいるJARをどこかに退避すれば起動するようになりたすが、そういう察凊もちょっずないですよね。このようにGroovyに含たれおいるJARず衝突しおどうにもならない堎合は、Gradleずかに行った方が良さそうです。

アヌティファクトなどの指定を動的にはできない

Gradleで䜿われるように、Grabで指定するバヌゞョンをたずめたりしようずしおGStringを䜿甚した以䞋の定矩を曞くず

def version = '3.3.2'
@Grab("org.apache.commons:commons-lang3:$version")
import org.apache.commons.lang3.StringUtils

println(StringUtils.join(['Hello', 'World'], ', '))

以䞋のように゚ラヌになりたす。

The missing attribute "group" is required in @Grab annotations
 at line: 2, column: 1

これ、GStringの評䟡の前にAST倉換がかかるので、Grabアノテヌション単品で指定されおおく必芁があるんでしょうねぇ 。

ちなみに、こういう曞き方もダメです。

def version = '3.3.2'
@Grab('org.apache.commons:commons-lang3:' + version)

さらに蚀うず、こういうのもダメです。

def ver = '3.3.2'
@Grab(group = 'org.apache.commons', module = 'commons-lang3', version = ver)

この堎合は、

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
/path/to/script.groovy: 4: Attribute "version" has value ver but should be an inline constant in @Grab annotations
 @ line 4, column 1.
   @Grab(group = 'org.apache.commons', module = 'commons-lang3', version = ver)
   ^

ず怒られたす。

groovycでコンパむルしおjavaコマンドで実行するのは、諊めた方がよさそう

解決できなかったネタです。

@Grabアノテヌションを䜿ったスクリプトを、groovycでコンパむルしおjavaコマンドで実行させるのは諊めた方がよさげです。

That darned "No suitable ClassLoader found for grab" with groovyc and java -cp
http://www.randomactsofsentience.com/2012/01/that-darned-no-suitable-classloader.html

クラスロヌダヌやGrapeの初期化たわりで゚ラヌになるので、Grapeを䜿う堎合はgroovyコマンドでスクリプト的に䜿甚するケヌスず割り切った方が安党そうです。

Grabアノテヌションで指定した䟝存関係を、䞀郚解決できずに倱敗する

これは、HTTPBuilderを䜿っおいおハマったこずがありたす。

GroovyのHTTPBuilderで、multipart/form-dataを送信する
http://d.hatena.ne.jp/Kazuhira/20140126/1390708336

圓時、HTTPBuilderを䜿甚する時にこんな感じに曞くず、

@Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.6')

以䞋のような゚ラヌをもらっおハマっおいたした。

org.codehaus.groovy.control.MultipleCompilationErrorsException

で、この時うたくいっおいなかったのは、䟝存しおいるcommons-codecを取埗できずに倱敗しおいたした、ず。

この時は、commons-codecをExcludeしおから自分で远加するず、なんずか動きたした。

@Grab('commons-codec:commons-codec:1.9')
@Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.6')
@GrabExclude('commons-codec:commons-codec')

なんですけど、今の自宅環境ではうたく再珟せずもずもず、䌚瀟の環境で芋぀けたので 。

同じ条件でハマっおいた方を芋かけたこずがあるので、䜕か原因があるんでしょうけど、この時ちゃんず远いかけお入ればよかったかなヌず思っおいたす。

なんですが、もしかしたら次に挙げるものに関連しおいたのかもしれたせんね。

Grapeが参照するMavenリポゞトリが、埮劙に䞍完党

これは、@eiryuさんがハマっおいたのを䞀緒に远いかけたネタですね。

Grapeの䟝存性解決に倱敗しおjcenterに問い合わせた話
http://eiryu.hatenablog.com/entry/20140430/1398874534

Failed resolving by Groovy's Grape
https://github.com/making/ltsv4j/issues/1

Grapeが参照するリポゞトリのうちのひず぀である、JCenterにpom.xmlはあるのにJARファむルが存圚せず、Ivyが䟝存関係を解決できなくなりハマり続けおしたうずいうパタヌン。

もずもず、Grapeの䟝存関係の解決順は

1. Grape's local cache
2. Maven local cache
3. Codehaus Maven repo
4. Maven Central
5. Java.net Maven repo

だったらしいのですが、Groovy 2.1.8から以䞋のようにJCenterが远加されたようです。

[GROOVY-6380] Improve Grapes resolution speed and stability by adding Bintray JCenter as first remote resolver in the chain
http://jira.codehaus.org/browse/GROOVY-6380

Codehausのリポゞトリが遅い、有名なラむブラリがあんたり入っおいない、ずいうずころから远加されたリポゞトリのようです。

ずいうわけで、今のGroovy 2.1.8〜少なくずも2.2.2Grapeのデフォルトの䟝存関係の解決順は

1. Grape's local cache
2. Maven local cache
3. Bintray's JCenter
4. Codehaus Maven repo
5. Maven Central
6. Java.net Maven repo

ずなりたす。

参考
https://github.com/groovy/groovy-core/blob/GROOVY_2_2_2/src/resources/groovy/grape/defaultGrapeConfig.xml

なんですが、先の@eiryuさんの゚ントリに茉せおあるltsv4jは、JCenterにはpom.xmlしかなく、JARファむルが存圚しない状態でした。

なので、予想ずしおは 

  • 䟝存関係を解決する際の、最初のルックアップ先でIvyがpom.xmlを芋぀ける
  • pom.xmlは芋぀かるものの、JARがないためそこで諊める

JARが芋぀からなければ、次のリポゞトリぞ行く なんお動䜜はしないず思いたすので、そこで延々ずハマっおいたのがこの時の事象ず思われたす。

こうなっおしたうず、暙準的にGrapeを䜿っおいるずい぀たでも解決できたせん。

ちなみに、デバッグは双方

$ grape -V install <group> <module> <version>

や

$ groovy -Dgroovy.grape.report.downloads=true -Divy.message.logger.level=4 <スクリプト名>

などでやっおいたした。

で、どう解決したか

解決策は、以䞋あたりが考えられたす。

  • 問題のあるMavenリポゞトリを修正しおもらう䞊蚘の堎合は、JCenter
  • Mavenであらかじめダりンロヌドしおおく未怜蚌 
  • 問題のあるMavenリポゞトリをGrapeの参照先から解陀する

最初の「問題のあるMavenリポゞトリを修正しおもらう」が、今回の解決方法ずなりたした。なんず、リポゞトリ偎に䟝頌しお察応しおもらえるずは 。

で、未怜蚌ですがGrapeではなくおMavenであらかじめダりンロヌドしおおけば、䟝存関係の解決時のルックアップ先にMavenのロヌカルリポゞトリが入っおいるので、なんずかなるのではないかなヌず思っおいたす。

最埌、「問題のあるMavenリポゞトリをGrapeの参照先から解陀する」ですが、こちらは自分が取った方法です。

以䞋のパスにGrapeの蚭定をGitHubなりからコピヌしおきお、今回の堎合はJCenterを削陀するずMaven Centralたで流れるようになりたす。

$HOME/.groovy/grapeConfig.xml

Maven Centralなどにアヌティファクトがある堎合は、これで解決するこずができたす。

 なんですけど、最埌の2぀の方法はGroovyスクリプトのポヌタビリティがなくなるので、あたり本質的な解決策ではありたせん。できれば、リポゞトリ偎が䞭途半端な状態にならないようにしおいただきたいずころですが。

速床アップのためにJCenterを远加したようなので、あんたりトラブルが出おしたうず、埮劙ですよね 。

その他、ハマりどころを芋぀けたら、たた远蚘したす〜。