mike-neckのブログ

Java or Groovy or Swift or Golang

daabの開発をKotlinでできるようにする

今いる会社の提供するdirectというチャットサービスではボットの開発ができて、そのsdkdaab(direct agent assist bot)というらしい。

で、daabチームが専修大学ハッカソンを共催するということで、僕も自分の会社のボットくらい作れないと恥ずかしいなと思ったので1日だけ参加してきました。


daabはhubotをdirect用に改造した感じのもので、次のようなプログラムで簡単にボットが記述できます。

module.exports = robot => {
    robot.hear(/^today/i, res => {
        var now = new Date();
        res.send(dateFormat(now, "yyyy/mm/dd"));
    });
};

ところが、困ったことにどういう関数があるのか、何を引数に取るのか、ドキュメントを漁ってもいまいちわかりません。

ドキュメントを漁ってるうちに、Typescriptでhubotを書きたい人なら型定義ファイルを作っているに違いないと思い立って、探してみたら下の記事を見つけました。

qiita.com


この記事にかかれていた型定義をそのままコピってTypescriptでdaabを書いてもいいんだけど、もうひとひねり入れて、ts2ktにかけてKotlinの型情報に変換してdaabをKotlinで書けるようにしてみました。

ts2ktをしてできた型情報(一部省略/修正)

@file:Suppress("INTERFACE_WITH_SUPERCLASS", "OVERRIDING_FINAL_MEMBER", "RETURN_TYPE_MISMATCH_ON_OVERRIDE", "CONFLICTING_OVERLOADS")
@file:JsQualifier("hubot")
package hubot

external interface Robot {

    fun hear(regex: RegExp, callback: ((res: Response) -> Unit)? = definedExternally /* null */)
    fun hear(regex: RegExp, options: Function<*>, callback: ((res: Response) -> Unit)? = definedExternally /* null */)
    fun hear(regex: RegExp, options: Json, callback: ((res: Response) -> Unit)? = definedExternally /* null */)

}

external interface Response {

    fun send(vararg strings: String)

}

ボット部分のコードは見事に綺麗なKotlinで書けました。

package com.example

import hubot.Response
import hubot.Robot
import kotlin.js.RegExp

val app: (Robot) -> Unit = { robot: Robot ->
    robot.hear(RegExp("now")) { res: Response ->
        res.send(dateTime())
    }
}

kotlin-jsはkotlinc-jsではなく、gradleでコンパイルしました。なお、 build.gradle はこんな感じ

buildscript {
    ext.kotlin_version = '1.1.2-4'
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'kotlin2js'

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
}
compileKotlin2Js {
    kotlinOptions.outputFile = "${projectDir}/daab/scripts/lib/daab.js"
    kotlinOptions.moduleKind = "commonjs"
    kotlinOptions.sourceMap = true
}
task generateAppJs {
    def jsFile = file("$projectDir/daab/scripts/application.js")
    outputs.file jsFile
    doLast {
        def content = """let kotlin = require("kotlin");
let daab = require("./lib/daab");
module.exports = daab.com.example.app;
"""
        jsFile.write(content, 'UTF-8')
    }
}
tasks.compileKotlin2Js.dependsOn tasks.foreverIgnore
tasks.compileKotlin2Js.finalizedBy tasks.generateAppJs

実行した結果は次のとおり、見事に動かせました

f:id:mike_neck:20170611113817p:plain


なお、気をつけるところがいくつかあって、KotlinのAPIJVM環境のプログラムを書く場合のAPIと若干異なっているところです。例えば Date 型はありますが、 java.util.Date ではなくて、 kotlin.js.Date になっていて getTime() メソッドも Long ではなく、 Double を返すなど異なっています。

今回の成果をもう少し汎用的にして、かつ、同僚のエンジニアにAPIを確認して型情報を明確にした上でgradleプラグインを出そうかなと思っています。

JJUG CCC 2017 Spring 参加メモ

表題の通りJJUG CCC 2017 Springに行ってきました。参加したセッションのメモです。

www.java-users.jp


エンプラ開発におけるレガシーアプリケーションの巻取りとモジュール分割の戦い

  • 既存のレガシーアプリケーションにアプリケーションを追加する案件から、機能を巻き取っていく過程の話
  • 普段勉強会などや事例集などで目や耳にする事例をどのように自分たちのプロジェクトに適用していくか普段から考え、備えておくことが重要とのこと

www.slideshare.net

データ履歴のためのテンポラルデータモデルとReladomoの紹介

  • システムにおける履歴データと事実としての履歴データを扱うためのReladomoの紹介
  • ゴールドマン・サックスが公開しているOSS
  • サンプルは見せてもらったけど、実施に自分で触ってみないことにはなんとも言えないかな

www.slideshare.net

文型さえおさえれば英語を読む力は上がる!

  • SV/SVC/SVO/SVOC/SVOOといった五文型(懐かしい)を抑えれば、英語を雰囲気で読むことはなくなるという話
  • Springのドキュメントが癖のある文章が多いそうなので、これが読めるようになると英語に強くなれるとのこと
    • 癖のある文章を読めるようになることで英語に強くなれるとは僕は思いませんが…

speakerdeck.com

Javaで実装して学ぶOAuth2.0!

  • JavaのOAuth2の参照実装であるOltuを通してOAuth2を理解する話
  • 大きなプレゼンでのデモには魔物が住むというのがよくわかった
  • OAuth2を抑えるにはまず正確に用語を理解することと、RFCを読むことが重要とのこと
  • 今回Spring Security OAuth2を使わなかった理由は、Springで発表する人が多くなってきてSpring一色になってしまうから

speakerdeck.com

Java8プログラミングベストプラクティス + きしだが働いているかどうかIDEのメモリ使用状況から機械学習で判定する

  • IDEのメモリ使用状況のログをフーリエ解析して、それを機械学習して、働いているかどうかを判定できるようにする話
    • オチが秀逸
  • Java8のベストプラクティス + Java7以前でも使えるベストプラクティス

思ったほど怖くない!Haskell on JVM超入門

  • JVMで動くHaskellのFregeとEtaの紹介
  • 純粋な世界でモナドがなぜ必要になるのかを詳しく説明
    • モッがこわいと言う人はこの資料を見たほうがよいかもしれない
  • FregeにおけるJavaオブジェクトの利用方法
  • EtaにおけるJavaオブジェクトの利用方法

www.slideshare.net


懇親会ではLINEさんがスシスポンサーをして、寿司職人さんがいる寿司がふるまわれました。

LTをしてきたのですが、元からネタがなかったので正直ウケませんでした(資料は作ってる途中)


今回はdoorkeepersでキャンセル待ちが発生するほど参加者が多くなってました。(てらださんのツイートによると参加者は1032人ほどだったそうです。)

以上

Java Day Tokyo 2017 参加メモ

Java Day Tokyo 2017に数年ぶりに参加しました。そのメモです。僕が参加したのは次のセッションです。

  • 基調講演
  • Java 9 and Beyond: Java Renaissance in the Cloud
  • Modular Development with JDK 9
  • Tuning G1GC
  • Java SE 9のすすめ
  • 緊急開催!Java技術メモ三銃士が語るエンジニア道
  • スペシャルパネルセッション - 海外Javaコミュニティ・エンジニアは今どんな活動をしているのか?

基調講演

Javaの現状と今後についてのポジショントーク。Java9/10についていろいろと情報がありましたが特に目新しい情報はありませんでした。目新しい情報がないということはJigsawがRejectされたことについても特に言及なしでした。Java9は7月にリリースできるのでしょうか…???

まあ、僕のような平凡なエンジニアができるのは

というところでしょう。これは別にJEPの実装をやれというわけではなく、early accessJDKを触ってフィードバックするなどでよいということです。

Java EE8 は7月にリリースされるということでしたが…


Java 9 and Beyond: Java Renaissance in the Cloud

Java9の概要とOracleが提供するツール類、Java10以降の説明。ざっと説明された内容は以下の通り。

  • Jigsaw
  • jlinker
    • カスタムランタイムや実行バイナリを作るツール
  • AOTコンパイラ
  • GC
    • G1GCがデフォルトになる
  • オラクルの提供するツール類
    • Flight Recorder/Mission Control/Advanced Management Console
  • Varhara
    • メモリ効率の向上
  • Panama

Modular Development with JDK 9

Jigsawについての説明。前から疑問に思ってたことが解決できた。

前からの疑問:既存のjarに対してどうやってモジュールに組み込んでいくか?

  • オートマティックモジュールという仕組みを利用して組み込む
    • jarの名前をそのままモジュール名に変換する(例: spring-boot-starter-web なら spring.boot.starter.web モジュール)
    • jarファイルをモジュールのあるディレクトリーに配置して -m オプションで指定する

なお、移行に関してもアドバイスが有りました。

  • jdk.unsupported にあるクラス(例えば sun.misc.Unsafe など)についてはモジュールで使う宣言をすれば利用できる
  • JDK内部のクラスを利用しているもの (sun.misc.Base64Encoder を利用しているなど) はもれなくアプリケーションが動かなくなるので、代替のクラスを利用すること
  • ClassLoader を使ってクラスの読み込みをカスタマイズしているものについても壊れる可能性があるので早めに対策すること
  • rt.jar などのJDKのjarの名前、パスに依存しているアプリケーションも死ぬ

Tuning G1GC

あまりにGCのことがわかってなくて、ちょっと理解できませんでした。実際英語もよく聞き取れなかったし、通訳さんもつらそうでした。また説明の途中で時間切れになって終わりました…

Java SE 9のすすめ

またもやJava9の話。もっとJava EEの話を聞くとかすればよかったかなとも思ってる。

  • Breaking Changes
    • _ が変数名としては使えなくなる
    • LogManager#addPropertyChangeListener などの一部のメソッドが使えなくなる
    • win-x86のclientがなくなる
    • Java DBが同梱されなくなる(Derbyが欲しければjarを落としてくる必要がある)
    • hprof/jhatがなくなる
    • JREの構造が変わるので rt.jar のパスに依存しているともれなく壊れる
    • 起動オプション -Xbootclasspath がなくなってクラスを差し替えて…(非推奨)
  • 新しい機能など
    • java.util.concurrent.Flow のインターフェース(リアクティブストリーム用)
    • jdk.incubator モジュールのhttp/2クライアント
  • コンパイラーの改善
    • tey-with-resourceの改善(下記1参照)
    • 匿名クラスのインスタンス生成時に型パラメーターをダイヤモンドで省略可能になる(下記2参照)
// 1. try-with-resource
// 従来
final Reader reader = ...
try (final Reader anotherVariable = reader) {
  // do something
}
// Java9 or later
final Reader reader = ...
try (reader) {
  // do something
}

// 2. anonymous class
// 従来
final Function<String, Integer> length = new Function<String, Integer>() {
  // ...
}
// Java9 or later
final Function<String, Integer> length = new Function<>() {
  // ...
}
  • APIの改善
    • Stream#ofNullable(x) / Stream#iterate(seed,hasNext,next)
    • Collectors#flatMapping / Collectors#filtering
    • Optional#stream - OptionalFoldable っぽくなる / Optional#or - Optional の合成 / Optional#ifPresentOrElse 男らしいメソッド…
    • List#of / Set#of / Map#of それぞれのスタティックファクトリーメソッド(やっと今頃)
    • Enumeration#asIterator - 生サーブレットを触っている人にはおなじみの Enumeration にやっと(10年ごしくらい) Iterator にできるメソッドが追加

緊急開催!Java技術メモ三銃士が語るエンジニア道

Q なぜメモを取り始めたか?

みな、誰かに読まれることというよりは自分のためにメモを取るという感じですね。

Q やっててよかったこと/やらなければよかったこと

書き続けることでセルフブランドが向上するということですね。

Q メモを続けるコツ

全員共通しているのは無理をしないということでした。

Q 技術情報の調べ方

ひしだまさん以外は基本的には公式のドキュメントにあたることが多いようです。ひしだまさんは使える・動くを主軸に考えて調べているようです。

質問コーナー

これは多分、質問の意図が理解できてなかったっぽい…

スペシャルパネルセッション - 海外Javaコミュニティ・エンジニアは今どんな活動をしているのか?

疲れてて、実はあまり覚えてない…

という感じでJava9をかなり多めに聞いてきましたが、この内容を元に今後も考えていきたいです。

Java9(ea163)でHttpClientを使ってみたノート

ディレクトリ構成やmodule-info.javaで少々ハマったので(10分)、ノート。


Java SE9から公式のやっと面倒くさくないHttp Clientが出るので試してみた。

試してみたのは jdk.incubator.httpclient モジュールの jdk.incubator.http.HttpClient とその関連クラス。

1. ディレクトリー構成

Java9からはモジュールを定義しておかないと、使いたいクラスがうまく利用できない。したがって、まずモジュール名を決定した後にモジュール名と同じ名前のディレクトリーを作り、その下に src ディレクトリーを作る。

まずモジュール名は com.example とした。

ディレクトリーを次のように作る。

root/
└── com.example
    └── src

2. モジュールの定義

src ディレクトリーの下に module-info.java を作る。ここには、公開するパッケージの定義と利用するモジュールを記入する。詳しい話は IT Proの連載記事 などを参考にした。なお、今回作るプログラムを配置するパッケージを com.example.ex とした。また、利用するモジュールは上述のとおり jdk.incubator.httpclient である。

module com.example {
  exports com.example.ex;
  requires jdk.incubator.httpclient;
}

現在のディレクトリー構成

root/
└── no.lib
    └── src
        └── module-info.java

3. プログラムを書く

適当に書く。

package com.example.ex;

import jdk.incubator.http.HttpClient;
import jdk.incubator.http.HttpRequest;
import jdk.incubator.http.HttpResponse;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HttpClientTest {

    public static void main(String[] args) {
        final ExecutorService executor = Executors.newFixedThreadPool(1);
        final HttpClient client = HttpClient
                .newBuilder()
                .version(HttpClient.Version.HTTP_1_1)
                .executor(executor)
                .followRedirects(HttpClient.Redirect.SAME_PROTOCOL)
                .build();
        final HttpRequest request = HttpRequest.newBuilder(URI.create("https://www.google.com/teapot"))
                .GET()
                .build();
        client.sendAsync(request,
                HttpResponse.BodyHandler.asString(StandardCharsets.UTF_8))
                .thenApply(HttpResponse::body)
                .thenApply(s -> String.format("[%s] - %s", Thread.currentThread().getName(), s))
                .thenAccept(System.out::println)
                .join();
        executor.shutdown();
    }
}

実行結果

WARNING: Using incubator modules: jdk.incubator.httpclient
[pool-1-thread-1] - <!doctype html><html class="google" lang="en"> <script>(function(H){H.className=H.className.replace(/\bgoogle\b/,'google-js')})(document.documentElement)</script> <meta charset="utf-8"> <meta content="initial-scale=1, minimum-scale=1, width=device-width" name="viewport"> <title>Error 418 (I&#8217;m a teapot)!?</title> <link href="//www.gstatic.com/teapot/teapot.min.css" rel="stylesheet"> <a href="//www.google.com/"><span aria-label="Google" id="logo"></span></a> <p><b>418.</b> <ins>I&#8217;m a teapot.</ins></p> <p>The requested entity body is short and stout. <ins>Tip me over and pour me out.</ins></p> <div id="teaset"><div id="teabot"></div><div id="teacup"></div></div> <script src="//www.gstatic.com/teapot/teapot.min.js"></script> </html>

Process finished with exit code 0

現在のディレクトリー構成

└── com.example
    └── src
        ├── com
        │   └── example
        │       └── ex
        │            └── HttpClientTest.java
        └── module-info.java

たまにモジュール内のパッケージの export 先が決まっていることがあったりするので、気をつけたい。