Javaの新元号対応を試す。そして実用には問題がある。

元号対応はNewEraという仮の元号で対応が進んでいるので、試してみました。
おそらく、実用では使えない場面がある仕様になっています。
なので、元号対応が必要そうな人は、早めに試して声をあげる必要があると思います。

ビルドする

※ 2018/6/16追記 ea18が出ているのでそのまま試せます
Masterブランチに取り込まれているので、明日あたりにでてくるea18で使えるようになると思いますが、いまは自分でビルドする必要があります。
ビルドについては、こちらを参考にしてください。ここに書いてるのとは違って、リポジトリhttp://hg.openjdk.java.net/jdk/jdkです。
http://d.hatena.ne.jp/nowokay/20171104#1509753358

使ってみる

JShellで使ってみます。

jdk $build/macosx-x86_64-normal-server-release/images/jdk/bin/jshell 
|  JShellへようこそ -- バージョン11-internal
|  概要については、次を入力してください: /help intro

jshell> import java.time.chrono.*

jshell> JapaneseDate.of(2018, 6, 14)
$2 ==> Japanese Heisei 30-06-14

jshell> JapaneseDate.of(2020, 6, 14)
$3 ==> Japanese NewEra 2-06-14


2020年の元号はNewEraになりました!
いまのところJapaneseEra.NEWERAは現時点ではprivateなので、直接使うことはできません。

問題点

たとえばぼくの免許の有効期限は平成32年3月までです。
これを扱おうとするとどうなるでしょうか?

jshell> JapaneseDate.of(JapaneseEra.HEISEI, 32, 3, 10)
|  Exception java.time.DateTimeException: year, month, and day not valid for Era
|        at JapaneseDate.of (JapaneseDate.java:231)
|        at (#8:1)


例外が発生します。これでは、ぼくの免許の有効期限はJavaでは扱えないことになりますね。現状で平成が続くことを前提とした2019年以降を対象とする文書はたくさんあります。こういった文書に記載されている年月が扱えないのは、問題になることもあると思います。
政府は2019年5月1日以降も一定期間 平成を使い続けるシステムが残る方向で調整を進めていますが、このようなシステムでJavaが使えなくなります。
改元後も「平成」利用へ 納税や年金システム、混乱回避:朝日新聞デジタル
また、JDK10までは問題なく扱えていてもJDK11以降で扱えなくなるということもあります。


もうひとつ、保険証券など長期の期限をもつものを扱うサイトで、「来年2040年は平成52年が期限と記載されているお客様は満期となります」のような表示をしようと思ったとき、JapaneseDateではこの処理ができません。
from(JapaneseEra, LocalDate)のようなメソッドを追加して、元号を指定して変換ができる必要もあるのではないかと思います。


元号に関わる処理は日本向けシステムを作っている人にしか影響がないので、日本で声をあげないと改善はされません。元号を扱う可能性があるなら、動作を確認して声をあげていかないといけないと思います。


※ 7/4 追記
パースに関してはLENIENTをResolverStyleに設定することでHeisei 32をパースできます。
下部のコードは次のような出力になります。

java.time.format.DateTimeParseException:
  Text '平成 32-1-1' could not be parsed:
  Invalid YearOfEra for Era: Heisei 32
2020-01-01
import java.time.LocalDate;
import java.time.chrono.JapaneseChronology;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.ResolverStyle;
import java.util.Locale;

public class LenientEra {
    public static void main(String[] args) {
        var dtf = new DateTimeFormatterBuilder()
                .appendPattern("GGGG y-M-d")
                .toFormatter(Locale.JAPANESE)
                .withChronology(JapaneseChronology.INSTANCE);
        try {
            System.out.println(LocalDate.parse("平成 32-1-1", dtf));
            System.out.println("Wrong behavior");
        } catch (Exception ex) {
            System.out.println(ex);
        }
        System.out.println(LocalDate.parse("平成 32-1-1",
          dtf.withResolverStyle(ResolverStyle.LENIENT)));
    }
}

福岡でおいしいビールが飲める店

という検索語で調べても、おまえ行ってないだろーとか単なるプロモーションだろーみたいなのしか出ないので、まとめておきます。
おいしいレストランでビールも3種類くらいあるよ、というのは載せてません。
あと、夏で暑いから、という理由でビールを飲みたいときは、普通のビアガーデンとかのほうがいいかも。
(2019/3/16更新)

ベースキャンプ

福岡で一番おいしいベルギービールの店です。ほとんどの銘柄が専用グラスで出ます。
大名にあります。
自分のところで樽を輸入したりしてます。
カレーの店という触れ込みですが、福岡で一番ベルギービールにこだわってる店です。

Ales

舞鶴にある、クラフトビールを置いているいい店。
IPAが多い印象。

FUKUOKA CRAFT

大名でエルボラーチョがやってるクラフトビールの店
自家醸造ビールが気軽に飲める福岡唯一の店。(オークラは遅くまでやってないし気軽にいける場所・雰囲気でもない)

BEER PADDY FUKUOKA

高砂のビアバー
福岡で一番いいクラフトビールの店です。
あまり広い店ではないので、大人数で行くには不向きです。
福岡で一番おいしい よなよなリアルエールが飲めます。(Alesにもある)

ブルワーズ

呉服町のビアバー
実はイベントがあるときにしか行っていないので、通常営業がどういう感じになっているかわからないのですが、たぶんおいしい。
たまにクラフトビールイベントをやっています。

クラフトビア・クリーク

今泉のビアバー
結構おもしろいビールを置いています。

Goodbeer STAND by Goodbeer faucets

博多駅近くにあって気軽に行けるけど、KITTEにあって割高。
Good Beer Faucetsは いつ復活するんすか?
追記:STANDではないGood Beer Faucetsは復活せずに閉店の模様・・・

== ビールにこだわってる店と言えるのはここまでかな ==

Cotton Fields

中州のビアバー。
種類は多いです。
以前は全部廣島での輸入だったので行かなくなったのだけど、久しぶりに行ったら輸入元が変わっていたので、たまに行ってもいいかなと思うようになった。

Morris

クラフトビールを置いててキャッシュオンの店
時期によって輸入元が違ったりして、というかふたりでデリリウムを頼んだら輸入元が廣島と大月で全然味がちがったということがあった。(廣島輸入は だいたいおいしくない)
あと、ベルギービールもボトルだけで出してくるので、グラスをもらったほうがいいです。

Hippo

リバレインの近くにある。

Black Sheep

大名にある。

Crafty Bear

キャナルの近くにある。
RubyKaigi後にみんなで行くなら一番いいかも

Red Fox

大名にあるっぽい。ここは行ったことない。

Hotel Okura

いま時点だと、福岡で唯一自家醸造のビールを市内で飲める店。
最近行ってないけど、バーだけになったのかな。

アサヒビール工場

竹下駅前にある、ビール工場併設のレストラン。
ジンギスカンとか食べる。
これがあるおかげで、博多駅まわりはスーパードライばかりなのだけど、よそで飲むスーパードライよりおいしかったりする。
もちろんビール工場で飲むスーパードライはおいしい。

銀座ライオン

エビスが何種類かある

エビスバー

博多駅の地下にある。
KITTEにある銀座ライオンと似たようなビールを出してる。

キリンビアホール 麦 BAKU

キリンビールが好きならいいんじゃないでしょうか。

バイエルン福岡(閉店中: 2019/3/16現在)

福岡で一番おいしいドイツビールの店です。ほとんどの銘柄が専用グラスで出ます。
地行というところにあります。
名前からわかるように、サッカーチームのバイエルンを応援する店ですけど、ドイツビールの品ぞろえが最高です。
場所的には不便ですが、わざわざ行く価値はあります。
レカロシートの席がありますが、レカロシートは運転で体を動かさないときに快適になるように作られていて、会話のように前のめりで体を動かすのには不向きなので、カウンターか奥の席にいったほうがいいです。
RubyKaigiな人向け情報としては、九州RubyKaigi02をやったこともあります。
RegionalRubyKaigi レポート (38) 九州 Ruby 会議 02

BRIM(閉店 2019/3)

大名にあるクラフトビールの店です。
あまりちゃんとクラフトビールを扱わなくなってきた印象

あげてない店

Beer Barre Soiree(結構ビールがあった)
シュタットマインツ(ドイツ料理とあわせてドイツビールがある)
五滴の膳(キリンのSpring Valley Breweryがおいてあった)
ラ・パニエ・ド・ニーム(よなよな などヤッホーブルーイング飲み放題をやっている)
KIRIN SOW-SOW(Spring Valle Breweryもおいてある。BAKUには ない)
D&Dマーケットテーブル(行ったことない)
BREWMASTER(好みじゃないのとサイト見てもやってるかどうかわからない。あと昼しかやってないので起きてないので行けない)
Irish PubやHalf Penny(ビールは扱ってる酒の一部でしかない感じなので)

ビールは知っておくと よりおいしく飲めます。

白熱ビール教室 (星海社新書)

白熱ビール教室 (星海社新書)

Java9、10でStringの+=に副作用があるバグ

Java 9、10でStringの+=にバグがあるということがStack OverFlowで報告されていました。
Why does array[idx++]+="a" increase idx once in Java 8 but twice in Java 9 and 10? - Stack Overflow


どういうバグかというと「s[i++] += i + ""」のようなコードが正しく動かないというものです。
次のコードを実行してみます。

public class PlusEqual {
    public static void main(String[] args) {
        System.out.print(System.getProperty("java.version"));

        String[] s = {"aa", "bb"};
        int i = 0;
        s[i++] += i + "";
        System.out.printf(" %s %d%n", Arrays.toString(s), i);
    }
}


Java 8では期待通りに動きます。

1.8.0_151 [aa1, bb] 1


ところが、JDK10.0.1では次のようになります。

10.0.1 [bb2, bb] 2


おもしろいのは、target=8とするとJDK10でもJava8と同じように動くところです。

10.0.1 [aa1, bb] 1


これは、「s[i++] += i + ""」が「s[i++] = s[i++] + i + ""」のように扱われていると考えることができます。
JDK11ea17で修正されています。JDK10.0.2はどうなるのかな。バックポートはされる予定。Java9はメンテナンスリリースは公開されないので、そのまま。
JDK-8204322 "+=" applied to String operands can provoke side effects - Java Bug System


で、このバグレポートはJEP280に関連づけられてるので、+演算子をdynamicInvoke使う実装にしたときにエンバグしたっぽいですね。
JEP 280: Indify String Concatenation
Java8までのJVMでは もちろん文字列結合のdynamicInvokeに対応していないので、target=8としたときには問題が発生しない、と。
なので、Java9やJava10.0.1でtarget=8したくない場合は-XDstringConcat=inline をつけてコンパイルすれば問題を回避することができるようです。

Javaのライセンスの現状をまとめた

なんか、「Javaが有償化された」「無償サポートがなくなった」「Oracleと契約するかJavaをやめるか」のような話がちらほら流れるようになってきて、事実とは異なってたり誤解があったり状況が妥当に伝わってない気がするなと思ったので、現状についてまとめてみた。
ちょっと話をする機会があったのと、現実問題としてちゃんと自分でも把握しておく必要もあったので。
もし他にもサポート提供する企業があるとか、そうは言っても現実的にはこうなんじゃね?みたいな話があればツッコミおねがいします。
Javaのサポートについてのまとめ - Qiita

JShellを使ってJava2Dであそぶ

JShellを使ってJava2Dであそんでみました。楽しい。


Javaが初めての人はこちらもどうぞ。
JShellで始めるJava - Javaの世界を覗いてみる - Qiita


ということで、まずjavax.swing.*をimport

jshell> import javax.swing.*


JFrameを用意

jshell> var f=new JFrame("guruguru")
f ==> javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden, ... tPaneCheckingEnabled=true]


そして、画像を表示するためのJLabelを用意

jshell> var label=new JLabel()
label ==> javax.swing.JLabel[,0,0,0x0,invalid,alignmentX=0. ... rticalTextPosition=CENTER]


画像としてBufferedImageを用意してますが、BufferedImageまで入力して[Shift]+[Tab]からの[i]を押すとimportのおすすめが出るので[1]を押してimportします。

jshell> var image=new BufferedImage
0: 何もしない
1: import: java.awt.image.BufferedImage
選択:
Imported: java.awt.image.BufferedImage


で、こんな感じでBufferedImageを用意

jshell> var image=new BufferedImage(400,300,BufferedImage.TYPE_INT_RGB)
image ==> BufferedImage@5ccddd20: type = 1 DirectColorModel ... 0 yOff = 0 dataOffset[0] 0


画像はImageIconを介してIconとしてJLabelに追加します。

jshell> label.setIcon(new ImageIcon(image))


labelをJFrameに追加したら、show()で表示

jshell> f.add(label)
$7 ==> javax.swing.JLabel[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.0,bo...sition=CENTER]

jshell> f.show()


録画領域からはみでていたので移動

jshell> f.setLocation(20,40)


packすると画面サイズが調整されます。

jshell> f.pack()


描画を行うためにGraphics2Dを取得

jshell> var g=image.createGraphics()
g ==> sun.java2d.SunGraphics2D[font=java.awt.Font[famil ... .Color[r=255,g=255,b=255]]


描画色を設定するのだけど、Colorはjava.awt.Colorなので、また[Shift]+[Tab]して[i]を押してimportを補完

jshell> g.setColor(Color
0: 何もしない
1: import: java.awt.Color
選択:
Imported: java.awt.Color


ここでは赤に設定します。

jshell> g.setColor(Color.RED)


線を引いてみる

jshell> g.drawLine(20,20,300,200)


labelをrepaintすると表示されます

jshell> label.repaint()


青で四角を描く

jshell> g.setColor(Color.BLUE)

jshell> g.fillRect(50,40,200,100)

jshell> label.repaint()


crearRectで一旦消去

jshell> g.clearRect(0,0,400,300)

jshell> label.repaint()


モアレを表示してみる。

jshell> IntStream.range(0,30).map(y->y*10).forEach(y->g.drawLine(0,y,400,300-y))

jshell> label.repaint()


画面消去

jshell> g.clearRect(0,0,400,300)

jshell> label.repaint()


ここもAffineTransformで[Shift]+[Tab]を押したあと[i]を押してjava.awt.geom.AffineTransformをimportしています。
{〜}の中は;(セミコロン)で行を終わらせる必要があるので注意。
で、最後の[}]を入力したら、ぐるぐる。

jshell> for(int i=0; i< 100;++i){
   ...> g.clearRect(0,0,400,300);

   ...> g.setTransform(AffineTransform.getRotateInstance(Math.PI*i/100,200,150));
   ...> g.fillRect(0,0,400,300);
   ...> label.repaint();
   ...> Thread.sleep(30);
   ...> }


最後に/listをしてぐるぐるに26というidが振られているのを確認して/26としてもう一度ぐるぐる。


Graphics2Dの使い方は、ドキュメント見て
https://docs.oracle.com/javase/jp/9/docs/api/java/awt/Graphics2D.html

Hyper-Vで仮想スイッチを作るとWavesのオーディオプラグインが死ぬ

Hyper-Vで仮想スイッチを作るとWavesのライセンスが無効になってしまいます。


どうも、WavesのライセンスはMACアドレスと結びつけているようで、Hyper-Vで別のMacアドレスのネットワークができることで別デバイスと認識されてしまうらしい。
Waves License Center(WLC)でライセンスが無効になる ( その他趣味 ) - 音響・映像・電気設備が好き - Yahoo!ブログ


ということで、追加した仮想スイッチを一旦削除するとライセンスが復活。そこでライセンスをクラウドに退避、仮想スイッチを作り直してライセンスを結び付けることで復活しました。


恐らくVirtualBoxなどで仮想ネットワークカードを作っても同じではないかと。
気をつけよう。
あと、これを利用して、常に同じMACアドレスの仮想スイッチと結びつくようにしておけば、マシンが変わってもライセンスを引き継げるぽい。

GraalVMでRust動かしたりレイトレをネイティブコンパイルしたり

GraalVMが正式にリリースされました。結構話題になってますね。
GraalVMは、Graal JITとAoT、そしてASTエンジンTruffleの複合体です。(かな?)
GraalVM


ということで、Rust動かしたりJavaで書いたレイトレコードをネイティブコンパイルしたりしてみました。

Hyper-VUbuntuを用意する

ほんとはWindows Subsystem of Linux(WSL)でやりたかったのだけど、WSL上でJavaがちゃんと動いてくれなかったのであきらめました。
で、VirtualBox使うかなと思ったけど、Hyper-Vを無効にしないといけなくて、Hyper-Vを無効にするとDockerが動かなくなるのでやだなーと思ってたのだけど、普通にHyper-VUbuntuたちあげればいいのではーと思ってやってみました。


普通に使えますが、画面サイズ調整やフォルダ共有、クリップボード共有など、VirtualBoxのほうが使いやすいですね。
Macの場合はCE版ではなくOracle版を使うといいと思います。

RustをGraalVMで動かす

ASTエンジンTruffleというのは、プログラム言語を構文木に落として、最適化・実行する処理エンジンです。
JavaバイトコードLLVMのビットコードよりも抽象度が高く言語の構造が残っているため、最適化がやりやすいということを狙ってます。(たぶん)
で、そこでLLVMのビットコードをTruffleコードに変換するというSulongというツールもあるので、Rustも動きます。ライブラリなんかは、x86コードをLLVMビットコードに変換してTruffleコードに変換して動かすらしい。変態。


で、こんな感じでRustコードをmain.rsという名前で用意します。

fn main() {
  println!("Hello rust! fib(8) is {}", fib(8));
}

fn fib(i:i64) -> i64 {
  if i < 2 {
    i
  } else {
    fib(i - 2) + fib(i - 1)
  }
}


とりあえず普通にコンパイルして動かしてみます。

$ rustc main.rs
$ ./main
Hello rust! fib(8) is 21


では、これをLLVMのビットコードを吐きだしてGraalVMで動かしてみます。

$ rustc --emit=llvm-bc main.rs
$ ls -l main.*
-rw-rw-r-- 1 naoki naoki 6860  4月 23 01:37 main.bc
-rw-rw-r-- 1 naoki naoki  146  4月 23 01:36 main.rs
$ graalvm-1.0.0-rc1/bin/lli --lib $(rustc --print sysroot)/lib/libstd-* main.bc
Hello rust! fib(8) is 21

動きました!


Haskellでもいけるかなと思ったけど、LLVM3.5が必要っぽく、パッケージが3.7からしかないっぽく、とりあえずあきらめ。

レイトレをネイティブコンパイルしてみる

GraalVMにはAoTコンパイラもあります。AoTというのはAhead of Timeで、事前コンパイルのことです。これを使ってJavaコードをネイティブコンパイルしてみます。
それなりに処理量があるコードということで、こないだ作ったレイトレを動かしてみます。
kishida/smallpt4j: smallpt Java port


最新版はいろいろいじってて複数ソースに分かれてたりするので、初期のものを使います。
https://github.com/kishida/smallpt4j/blob/original/src/main/java/naoki/smallpt/SmallPT.java


パッケージをデフォルトパッケージに変更しておきます。また。FastMathを使っているのでjava.lang.Math.*をstatic importしてこちらを使います。
で、普通にコンパイルして実行

$ graalvm-1.0.0-rc1/bin/javac SmallPT.java
$ graalvm-1.0.0-rc1/bin/java SmallPT

画像ができました。

今回は早く終わるよう、画像を荒くしたままにしてます。


ではネイティブコンパイルを。
ネイティブコンパイルにはzlib.hが必要なので、とってきます。

$ sudo apt install zlib1g-dev


そしてnative-imageでコンパイル

$ graalvm-1.0.0-rc1/bin/javac SmallPT.java 
$ graalvm-1.0.0-rc1/bin/native-image SmallPT
Build on Server(pid: 7220, port: 26681)
   classlist:     166.86 ms
       (cap):     544.93 ms
       setup:     803.27 ms
    analysis:   3,460.75 ms
error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Unsupported method java.lang.ClassLoader.getParent() is reachable: The declaring class of this element has been substituted, but this element is not present in the substitution class
To diagnose the issue, you can add the option -H:+ReportUnsupportedElementsAtRuntime. The unsupported element is then reported at run time when it is accessed the first time.

なんかエラー。
ImageIOがだめっぽい。動的にクラスロードしてるからだろうな。
※ 2018/11/14追記 GraalVM Community Edition 1.0 RC9ではImageIOに対応していてそのままnative-imageが通ります。


ということで、ImageIOをやめてオリジナルのsmallptと同様にppmを吐き出すように変更します。

        try(BufferedWriter bw = Files.newBufferedWriter(Paths.get("image.ppm"));
            PrintWriter pw = new PrintWriter(bw)) {
            pw.printf("P3\n%d %d\n%d\n", w, h, 255);
            for (Vec v : c) {
                pw.printf("%d %d %d ", toInt(v.x), toInt(v.y), toInt(v.z));
            }
        }

ということで、全体のコードはこちらに
SmallPT4j without ImageIO


改めてJavaで動かします。

$ graalvm-1.0.0-rc1/bin/javac SmallPT.java 
$ graalvm-1.0.0-rc1/bin/java SmallPT 
JVM:1.8.0_161 sample:40
Samples:40 Type:master Time:PT24.042S


これをネイティブコンパイル

$ graalvm-1.0.0-rc1/bin/native-image SmallPT
Build on Server(pid: 7220, port: 26681)
   classlist:     167.94 ms
       (cap):     592.71 ms
       setup:     808.36 ms
  (typeflow):   2,479.49 ms
   (objects):     875.25 ms
  (features):      24.48 ms
    analysis:   3,439.52 ms
    universe:     134.33 ms
     (parse):     318.66 ms
    (inline):     550.73 ms
   (compile):   2,101.23 ms
     compile:   3,249.17 ms
       image:     466.18 ms
       write:     120.34 ms
     [total]:   8,412.47 ms
$ ls -l smallpt
-rwxrwxr-x 1 naoki naoki 5696832  4月 23 02:27 smallpt

こんどは いけました。ネイティブファイルができてますね。そして5MB。案外小さい。


実行してみます。

$ rm image.ppm 
$ ./smallpt 
JVM:null sample:40
Samples:40 Type:master Time:PT37.341S
$ ls -l image.ppm
-rw-rw-r-- 1 naoki naoki 8244655  4月 23 02:28 image.ppm

というか、遅くなっている。。。24秒だったのが37秒に。


ついでに、通常のOpenJDKで動かしてみます。

$ jdk1.8.0_171/bin/java SmallPT 
JVM:1.8.0_171 sample:40
Samples:40 Type:master Time:PT18.565S
$ jdk-10.0.1/bin/java SmallPT 
JVM:10.0.1 sample:40
Samples:40 Type:master Time:PT16.400068S

GraalVMのJVMより速いし、JDK10はさらに速い!


ということでこんな感じに。

GraalVM native-image JDK8 JDK10
1.8.0_161 null 1.8.0_171 10.0.1
24.042 37.341 18.565 16.400068

というか、Durationの精度もJDK10であがってますね。


ということで、いろいろやってみました。
Swingアプリも試してみたけど、AppContextの初期化でインプットメソッドの処理をしようとしたときに動的クラスロードしてるっぽくてそこでダメですね。
まだまだ問題はありそうだけど、もっと開発が進むと面白そう。