Maven vs Java9のModule(Project Jigsaw) : 依存はどっちが勝つの? (追記:あんまりMaven関係なかった)
無事Moduleを使ったビルドがMavenでできたところで、本当にやりたかった検証へ。
Mavenで複数プロジェクトが同一プロジェクトに依存していると、「どれか一つ」が使われることになります。
全て同じバージョンなら問題にならないのですが、それぞれ異なるバージョンのプロジェクトを使用していると、結果、意図せぬ不具合を引き起こすことになります。
もしかしてmoduleはこの問題も解決したのでは? と期待したのですが… 結果、ダメそうでした!
検証内容
こういう依存関係のものを作ります。
MultiVersion.java
package multi.version; public class MultiVersion { public static String version () { return "1"; } }
multi.version/pom.xml(一部)
<groupId>multiversion</groupId> <artifactId>multi.version</artifactId> <version>1.0</version>
World.java
package org.astro; import multi.version.MultiVersion; public class World { public static String name() { return "world" + MultiVersion.version(); } }
org.astro/pom.xml (一部)
<dependencies>
<dependency>
<groupId>multiversion</groupId>
<artifactId>multi.version</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
Main.java
package com.greetings; import org.astro.World; public class Main { public static void main(String[]args){ System.out.format("Greetings %s!%n", World.name()); } }
ビルドして実行すると
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Greetings world1!
ここまではOK.
さてここで、
multi.versionモジュールのversion2.0を作ります。
依存を追加するだけで、特にrequiresやimportはしません。
MultiVersion.java
package multi.version; public class MultiVersion { public static String version () { return "2"; } }
multi.version/pom.xml
<groupId>multiversion</groupId> <artifactId>multi.version</artifactId> <version>2.0</version>
そして com.greetingsのpom.xmlにversion 2.0のmultiversionへのdependencyを追加します。
<dependencies> <dependency> <groupId>groupId</groupId> <artifactId>org.astro</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>multiversion</groupId> <artifactId>multi.version</artifactId> <version>2.0</version> </dependency> </dependencies>
GitHub - taichiw/moduleTry at 3.0_module_tukattemo_dame
これで実行すると…
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Greetings world2!
2が返ってきてしまいました。
たとえModuleを使っても、Mavenビルド時に、「より外側」に書かれたプロジェクトが使われてしまうことに変わりはないようです。
これだと、Moduleは、Module提供者が意図とした環境での実行が行われるとは限らないことになります。
さらに、MultiVersionクラスのメソッド名を変えてみます。
MultiVersion.java
package multi.version; public class MultiVersion { // changed method name version() -> version3() public static String version3 () { return "3"; } }
これでもビルドは通りますが…
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Exception in thread "main" java.lang.NoSuchMethodError: multi.version.MultiVersion.version()Ljava/lang/String; at org.astro.World.name(World.java:6) at com.greetings.Main.main(Main.java:7)
実行時エラーになってしまいました。
残念。
追記 (2017/10/05)
Mavenが悪いみたいな書き方したけど、そもそも現行のModuleの仕様の問題っぽい気がする。
mavenで作られたjarファイルを1ディレクトリに集めて…
$ ls mlib com.greetings-1.0-SNAPSHOT.jar multi.version-1.0.jar org.astro-1.0-SNAPSHOT.jar
javaコマンドでmoduleとして実行すると
$ java -p mlib -m com.greetings/com.greetings.Main Greetings world1!
multi.versionを2.0に置き換えて実行すると
$ cp multiversion/target/multi.version-2.0.jar mlib/ $ mv mlib/multi.version-1.0.jar mlib/multi.version-1.0.jar_ $ ls mlib/ com.greetings-1.0-SNAPSHOT.jar multi.version-1.0.jar_ multi.version-2.0.jar org.astro-1.0-SNAPSHOT.jar $ java -p mlib -m com.greetings/com.greetings.Main Greetings world2!
2になっちゃう。
moduleをビルドするときには--module-versionコマンドでバージョン指定しながらモジュールが作れるので、特定のバージョンに対しての依存も貼れそうなんだけど…
ひとまず方法が見つからず。
追記
今日、JavaOneでセッションがあったようで… あとで資料 or tweet or なんか無いか探してみよう。
MavenでJava9のModule(Project Jigsaw)を使ってみた
探し方が悪いのか、わざわざ書くほどのことでもないのか、mavenでJava9のmoduleを使うとどうなるか という記事が英語でも日本語でもWebで見つからなかったので、試してみました&備忘録としてブログに残しておきます。
Project Jigsaw: Quick Start Guide
こちらのQuick Startのソースを拝借しました。
結論から言うと、「module-info.javaを置いておけばmoduleになる」っぽい。
(言葉の使い方が正しいのか怪しい…)
1. module使わない版
とりあえず比較用に、"Java9で導入されたmodule" ではない、ただの独立したJavaリポジトリが2つの状態。
GitHub - taichiw/moduleTry at 0.1_no_module
World.java
package org.astro; public class World { public static String name() { return "world"; } }
Main.java
package com.greetings; import org.astro.World; public class Main { public static void main(String[]args){ System.out.format("Greetings %s!%n", World.name()); } }
com.greetings/pom.xml (一部)
<dependencies> <dependency> <groupId>groupId</groupId> <artifactId>org.astro</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
org.astro -> mvn install
com.greetings -> mvn package
でビルド成功。
$ java -jar com.greetings/target/com.greetings-1.0-SNAPSHOT-jar-with-dependencies.jar Greetings world!
2. org.astro (libとして提供される側)だけmoduleにしてみる
org.astro/module-info.java
module org.astro { exports org.astro; }
これでも普通にビルドが通る。
3. org.astroをただのjar, com.greetingsをmoduleにしてみる
何もrequiresしない場合
com.greetings/module-info.java
module com.greetings { }
これはビルドエラー。
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project com.greetings: Compilation failure: Compilation failure: [ERROR] ~/moduleTry/com.greetings/src/main/java/com/greetings/Main.java:[3,17] package org.astro does not exist [ERROR] ~/moduleTry/com.greetings/src/main/java/com/greetings/Main.java:[7,54] cannot find symbol [ERROR] symbol: variable World [ERROR] location: class com.greetings.Main
module-info.javaを書いた瞬間にmodule扱いになる → requiresなしでは外部パッケージが使えなくなる
ということらしい。
requiresを足してみたら
com.greetings/module-info.java
module com.greetings { requires org.astro; }
org.astroというmoduleは存在しないにも関わらずビルドが通ってしまった…
IDE上では思いっきり赤線が引かれているのでなんか間違ってるかも。
4. org.stroもcom.greetingsもmoduleに。
org.astroパッケージをexportする。
GitHub - taichiw/moduleTry at 1.0
これはきれいにビルド成功。
(仕事の話です)剣道とサッカーと交渉ごとって似ていると思うんですよ
私、個人的に中学から大学まで、と少し期間が空いた後、おとなになってから数年間、剣道をやっていました。
(ここ一年半くらいサボり中)
多くの方は剣道に馴染みがないと思うのですが、競技としての剣道は非常にロースコアゲームで、「一本」がなかなか取りにくい競技です。
その為、左から攻めたり右から攻めたり、速く動いてみたりゆっくり動いてみたり
なんとか相手を揺さぶって、スキを作って、「一本」を取ろうとします。
が、自分ではあれやこれややってるつもりでも、(剣道で)先生と稽古すると全然崩れないし、苦し紛れに「め~~~ん」なんて打っていこうものならあっさり応じられる。
で、
「攻めが足りない」
なんて言われるんですが そもそも「攻め」ってなんだかがよくわからないのです。
本を見てもググっても、それっぽいことが書いてあるような無いような感じで…。
結局、「稽古の中で体得する他無い」という世界なんですね。
今でもさっぱりわからないので、これだけ長くやっていてもまともに試合で勝った経験というのが殆どないのですが…
一方のサッカー。
私自身は小学校の昼休みにボールを追いかけていた程度の経験と、国際試合をたまーに見る程度の素人中の素人なんですが…
一点を取るために、左右のサイドから攻めたり、緩急をつけてみたり…。
これを見ていた時にある日突然、あ 剣道と似ているなぁと 思いました。
(おそらく大概の格闘技やスポーツが、似たような要素をもっていると思うのですが、「なかなか点が入らない」という点において似てるんですよね、この2つ。)
本題
お仕事をしていると、意見が対立する人と話し合ってなんとか落とし所を見つけなければいけない時、ありますよね。
エンジニアの世界だと、チーム内でコードレビューの際に意見がすごく割れるとか、設計方針で議論になるとか といったことがあると思います。
また、クライアント側とAPI側など、立場が違うと「これはそっちがもったほうが良いんじゃ…」「いやいやそっちが…」的な押し付け合いに若干なったりとか。(逆に引っ張り合いも然り)
このような場合、
・自分の主張がどれだけ素晴らしいか
を理解してもらうためには
・相手は何を求めているのか
・相手が譲れないポイントと、譲ってもいいと思っているポイントはどこか
・自分の本当の目的は?
・自分たちの本当の目的は?
みたいなことを把握しつつ、すり合わせしつつ 話を進めて行かないといけないです。
交渉術… と言ってしまうと口先だけの野郎のような印象がしてしまうのですが、
悪い意味でなく、相手と適切に議論するためのスキルや『お作法』というものはあると思います。
このようなスキルやお作法。人に教えるのってすごく難しい、と最近感じています。
実際の議論の場面で、「今回の状況でどうするのが良さそうか」は教えられますが、それを一般化・体系化して身につけるためには本人の中で腹落ちするしか他ない。
逆に、本などで一般化された知識を身に着けても、実際にどう使うか は 何度か繰り返し使ってみないとわからない。
まさに、「稽古の中で体得する」しか無いのだと思います。
ということで、剣道とサッカーと交渉ごとは似ている… というお話でした。
RESTful APIってなんか意味あんの? って言うタイトルのブログを書こうと思ってるんだけど
Date型をTRUNCするとIndexが効かないよ という話
これ自体は昔から聞いたことがあったのですが、イマイチずっと理由がわかってなくて。
単純に、
OptimizerはTRUNCすら何か知らない
というのが原因なんですね。
JavaDayTokyo 2017 Report 2 : Reactive Stream
Reactiveなんちゃらというのがどうもわかりません。
という私が、Pivotalの槙さんのお話を聞いて、随分わかったかも!という気持ちになったセッション。
改めてブログにまとめなおしてみるとやっぱりわかってないところが多々あるのですが、ひとまず忘れないうちにメモを転記します。
発表資料はこちら。
Spring Framework 5.0による Reactive Web Application #JavaDayTokyo
BlockingとNon-Bloking
同期・非同期にかかわらず、基本的にJavaから他のI/O処理を呼び出した場合、レスポンスが返ってくるまでの間、呼び出し元のメインスレッドはお休み中になってしまいます(=Bloking)。
つまり、リソースの無駄遣いが発生するわけですが…
これと対照となるのが、イベントループを用いたNon-Blokingな処理。
これを実装しているアプリケーションサーバに、Nettyというサーバがあるそうです。
Appleでは35億65万台のNettyサーバが動いてるんだとか…。
Reactive Stream
Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure. ということで、「Reactive Stream」自体はinitiative(=そういう「動き」的なニュアンスなのかな?)らしいのですが
- non-blocking
- かつ back pressure
- な 非同期処理の仕様を策定しよう!
という動きと解釈して良さそうです。
back pressure というのが肝で、Subscriber(データを受け取る方)が、自分の処理能力に合わせて、「これだけちょうだい」って依頼する考え方だそうで。
依頼したあとデータが送られてくるのは非同期な、NonBlockingな処理なのですが、こっちの都合を無視してバンバン投げつけてこられるわけではない という考え方のようです。
Ractor
Reactive Streamの実装としてPivotal社が開発しているのが、Reactor。
他に、Reactive StreamとしてはRxJavaが有名ですが、RxJavaはJava6をベースにしているのに対し、ReactorはJava8ベースなのが一つの特徴とのことです。
Spring Framework 5.0でのReactive Spring
従来のSpringMVCはServletをベースにしているため、どうしてもBlockingなアプリケーションになってしまう。そこで、Spring WebFluxと言うものを新たに作りました!
返り値がFlux
その他、String以外のObjectや、Streamを返すことも可能とのこと。
Streamを返した場合、無限ストリームを返すことも可能だそうです。この場合でも、逐次処理なので、OutOfMemoryErrorになるようなことはないそうです。
この、無限ストリームを返す機能と、WebClient(Non blockingなHttpクライアント)を組み合わせると、例えば無限にTwitterのツイートを流し続けるようなアプリケーションも作ることが可能とのこと。
性能比較
リクエストを受けてから1秒Sleepしてレスポンスを返すようなアプリを、SpringMVCとSpring WebFluxで作ってみた場合。
Blockingアプリケーションの場合、1秒のスリープ中はスレッドが遊んでしまうにも関わらず、Blockされてしまうため、上限の200スレッドを超えたリクエストは待ちになってしまいます。
一方、NonBlockingなWebFluxの方は、リクエストごとにスレッドを作るわけではないため、TPSが200以上出ています。
実は、個人的にここ1-2年、よそのAPIを呼んで待ってるだけの、遊んでるスレッドは一つの課題でした。大してリソース食わないけどやたらスレッド数は増えちゃうし、先で詰まっちゃうと釣られて死んじゃう可能性があるし、それを避けるためにはサーキットブレーカーなり何なりが必要になるし…
ということで、発表中にもあったのですが、API Gatewayのような中間に挟まるアプリケーションにはすごく可能性がある話だと感じました!
なお、残念ながら、JDBCには現在Blockingなものしかなく、RDB接続をするアプリケーションの場合、完全にNonBlockingにするのは現時点では不可能、とのことでした。
(Java10で提案はあるそう)
ちょうど今の自分の課題にも関連しそうな技術なので、早めに手を動かしてみて、Updateしたいと思います。
JavaDayTokyo 2017 Report 1 : Java9 関連
今年もJavaDayTokyoに出席させていただきました。この記事ではJava9関連についてまとめます。
Oracle的な目玉はJigsawとjshellでした。
が、
Jigsawは去年のJavaDayTokyoで集中的に話を聞いたけどあまり使いどころがないなぁ… と感じ、
jshellはあると便利だけどこれまた本番で使うようなものではない(あと、以前に触ってみた感じ、そんなに使い勝手いいとも思えない… 変わってるのかな)
ということでこの2つは外して話を聞いてきました。
そんな私に櫻庭さんの、こちらのセッションがドンピシャでした。
D1-A4 Java SE 9のすすめ
Compatibility
いままで一生懸命後方互換性を守り続けてきたJavaがついに一部捨てた… と言うのはすごい分岐点だとは思うのですが、捨てられるだけあって何それなやつは多く。
Java DBってなんや…
Milling Project Coin
Project Coin (Java7に主に取り込まれた、ダイアモンドなど、Javaを書きやすくするプロジェクト)を更にすすめるもの。
Milingがギザギザをつける なので 「『ギザジュウ』作ろうぜ!」的なニュアンスらしいw
StreamAPIで書きにくいよねーっていうのが一部書けるようになった
- dropWhile
- takeWhile
- ofNullable
特にtakeWhileは使いそう。条件満たしたら終了。
これができなくてWhileとか、for + breakが結構残ってる感じ。
Optional
- stream
- ifPresentOrElse(action, elseAction)
- or(supplier)
ifPresentOrElseは使えそうですね。Nullのときの処理をelseとして記述可能に。
CollectionのFactoryメソッド
ofで初期化ができるようになる!
public static final List<String> PLAYERS = List.of("則本", "嶋", "銀次"); public static final MAP<Integer, String> PLAYERS = Map.of(14, "則本", 37, "嶋", 33, "銀次");
もうArrays.asListとか書かなくてよし。
Mapも初期化できるようになったので、何回もputとかしなくてよし。
(テストコードも書きやすくなるんじゃないかなー)
ポイントとして
ofで作ったコレクションはイミュータブル。Javaの標準仕様でイミュータブルなコレクションが扱えるのは初なんだとか。
(これって実装クラスは何が返るんだ? ※あとで確認)
String関係
表立っての変更ではないけど、なかなかおもしろいです。
内部の実装がcharの配列からbyteの配列へ
今までは全ての文字をUTF-16のコードポイント(=16バイト)で表していたのだけど、アスキー文字の場合、8バイトあれば十分なのでbyte型の配列で表すことにした、というもの。
これは結構メモリの使用量変わってくるのでは。
(じゃあ逆に16バイト必要な文字はどう収まるんだろう?byte変数を2個使うのかな? それともそういうときはchar? ※あとで確認)
だったら逆に、サロゲートペアも、もう少し自然に見えるようにしてくれよ… って思ったけど無理か。
+演算子での結合が StringBuilderに置き換え → invokeDynamicに置き換え
いちいちStrinbBuilderで結合してtoStringする時代が終わるのかしら
で、いつリリース?
今の予定では7月27日… ですが、結局JavaOneのころまでずれるんじゃないの… とのことでした。
今年は、これからド新規でいろいろなものを作っていくので、是非 9 を使いたいところなんですが… 果たして。