AirframeのFinagleサポートを試してみる

AirframeScala向けのDIコンテナを中心とした様々な便利機能を提供するプロジェクトなのですが、最近airframe-httpとairframe-http-finagleというモジュールが追加され、Finagleを使ったWebアプリケーションを簡単に作れるようになったので感じを掴むために軽く試してみました。

まず、build.sbtの依存関係はこんな感じ。

libraryDependencies ++= Seq(
  "org.wvlet.airframe" %% "airframe-http"         % "0.66",
  "org.wvlet.airframe" %% "airframe-http-finagle" % "0.66",
  "org.wvlet.airframe" %% "airframe"              % "0.66"
)

Webアプリケーションの実際の処理を作ります。ルーティングやパラメータのマッピングなどはアノテーションで指定する方式です。また、ケースクラスを返すと自動的にJSONレスポンスを返してくれるようです。とりあえずハローワールド的な感じでGETメソッドでパスパラメータを1つ受け取るだけのエンドポイントを定義しています。

import wvlet.airframe.http._

object MyResource {
  case class User(name: String)
}

trait MyResource {
  import MyResource._

  @Endpoint(path = "/user/:name", method = HttpMethod.GET)
  def getUser(name: String): User = User(name)

}

これを以下のようなMainオブジェクトで実行します。最小構成だとこんな感じですが、動作に必要なコンポーネントはAirframeのDI機能で合成されているので、いろいろカスタマイズすることが可能です。

import wvlet.airframe.http._
import wvlet.airframe.http.finagle._

object Main extends App {
  val router = Router.of[MyResource]

  val module = finagleDefaultDesign
    .bind[MyResource].toSingleton
    .bind[Router].toInstance(router)
    .bind[FinagleServerConfig].toInstance(
      FinagleServerConfig(port = 8080))

  module.build[FinagleServer] { server =>
    server.waitServerTermination
  }
}

ひとまずこれで動作を確認することができました。

Finagleを直接使うのはまあまあしんどいので、普通のWebアプリっぽく実装できるのはいいですね。また、httpサポートとfinagleサポートのモジュールが分割されているので将来的に他のバックエンドで動かすということもできるようになるかもしれません。

JIRAのチケット番号をリンクに変えるChrome拡張が便利

f:id:takezoe:20180918232337p:plain

仕事でもOSS活動でもJIRAを使うことがよくあるのですが、JIRAのチケット番号を自動的にリンクにしてくれるChrome拡張を見つけたので試してみました。

chrome.google.com

リンク先のJIRAは以下のような画面で設定できます。

f:id:takezoe:20180918231942p:plain

簡単なものですが、GitHubGmailなど様々な場面でチケット番号がリンクになるのでなかなか便利です。ただ、リンク先のJIRAが1つしか設定できないので、プロジェクト毎にリンク先のJIRAを設定できるともっと便利になりそうです。

Docker/Kubernetes 実践コンテナ開発入門

職場では様々な用途にDockerが活用されていたり、今後はKubernetesを使いそうな気配もあるので予習しておこうと思い、最近出たばかりのこの書籍を購入してみました。

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

見た目は結構厚い本ですが、400ページほどなのでそこまで大ボリュームというわけではありませんが、DockerからDocker Compose、Docker Swarm、Kubernetes、そしてクラウドベースのマネージドサービスまでこの分量でよくまとまっています。ハウツー的な解説に加えて概念的な部分や、なぜこのような設定を行うのかといった部分がきっちり抑えられており、きちんと理解しつつ読み進めることができます。コラムも実践的で、著者の方の深い知識と実戦で培われたであろう経験に裏打ちされた良書だと思います。

唯一残念な点があるとすると索引でしょうか。索引を作ると言うのはなかなか面倒な作業で(大半は編集さんの仕事ですが)、自分が本を書くときは索引なんて適当で良いのではないかと思ってしまうこともあるのですが、読者の立場になってみると設定項目からページを引いたりと思いの外利用頻度が高かったりします。この本の索引はちょっと荒過ぎて使い物にならない感じです。ただ、電子書籍版であればテキスト検索でカバーできるかもしれません。

ともあれこれからDocker/Kubernetesを触ってみようという方はこれを読んでおけば間違いないのではないでしょうか。

クローリングハックの韓国版が出ました

前職の同僚諸氏と執筆したクローリングハックという書籍が韓国語に翻訳されたとのことで翔泳社さんから一冊お送りいただきました。

f:id:takezoe:20180909185430j:plain

表紙などのデザインモチーフは日本語版ではクモだったのですが、これは…カニ???

まあ、クモがちょっとというのはわかるのですが、いったいなぜカニに…。見た目が似てるからでしょうか。

GitBucket 4.28.0をリリースしました

Scalaで実装されたオープンソースのGitサーバ、GitBucket 4.28.0をリリースしました。

https://github.com/takezoe/gitbucket/releases/tag/4.28.0

プラグインインストール時にプロキシを利用可能に

プラグインリポジトリからのインストール設定を"System settings"に移動し、HTTPプロキシの設定を行えるようにしました。

f:id:takezoe:20180901095222p:plain

今回のバージョンでは以下のような大きなバグをいくつか修正しています。

  • プルリクエスト作成時の画面でプルリクエスト先/プルリクエスト元リポジトリの変更時にInternalServerErrorが発生することがある
  • ダッシュボードでのイシュー/プルリクエストの一覧表示にStackOverflowが発生することがある

該当のバグでお困りの場合はアップグレードを検討していただければと思います。修正内容の詳細についてはIssueの一覧をご覧ください。

Scaladocを実行時に読み取れるようにするコンパイラプラグイン

ScalaベースのフレームワークでクラスのメタデータからSwaggerなどを生成できるものがあるのですが、専用のアノテーションやオブジェクトを使って情報を記述しておく必要があり、特にパラメータの説明などはScaladocに書いたものをそのまま使えればいいのに、と思うことがありました。

そこで試しにScaladocとして記述したコメントを実行時にリフレクションで取得できるようにするコンパイラプラグインを作ってみました。

github.com

プロジェクトに以下の設定を追加しておくと使えるようになります。

libraryDependencies += "com.github.takezoe" %% "runtime-scaladoc-reader" % "1.0.1"

addCompilerPlugin("com.github.takezoe" %% "runtime-scaladoc-reader" % "1.0.1")

このコンパイラプラグインコンパイル時にScaladocの内容を@Scaladocアノテーションとしてクラスに埋め込みます。たとえば以下のようなクラスがあるとします。

package com.github.takezoe

/**
 * Hello, World!
 */
class HelloWorld {
  ...
}

このクラスから、実行時に以下のようにしてリフレクションでアノテーションを取得することでScaladocコメントを取り出すことができます。

import com.github.takezoe.HelloWorld
import com.github.takezoe.scaladoc.Scaladoc

val clazz = classOf[HelloWorld]
val scaladoc = clazz.getAnnotation(classOf[Scaladoc])

if(scaladoc != null){
  val comment: String = scaladoc.value()
  println(comment)
}

ClassだけでなくMethodFieldからも同じようにしてScaladocを取得できます。

ただし、/**から*/までがそのまま入っているので、実際にデータとして使うにはさらにScaladocをパースしてコメント記号を取り除いたりScaladocタグを抽出したりする必要がありますね。手軽に使えるScaladocパーサがあるとよいのですが…。

相対時間で表記した期間をパースするJavaライブラリを作ってみた

正確に言うと自分で作ったのではなく、AirframeというScalaライブラリに相対時間で表記した期間をパースしてくれる機能があるのですが、わけあってこれをJavaにポーティングしてみました。

github.com

Maven Centralにdeployしてあるのでpom.xmlに以下の依存関係を追加すれば使えます。

<dependencies>
  <dependency>
    <groupId>com.github.takezoe</groupId>
    <artifactId>jtimewindow</artifactId>
    <version>1.0.0</version>
  </dependency>
</dependencies>

実際の使い方はこんな感じです。オフセットは基準となる現在日時で、指定しなかった場合はシステム日付が使用されます。期間はZonedDateTimeだけでなくInstantやエポックタイムで取得することもできます。

import com.github.takezoe.jtimewindow.*;

TimeWindowBuilder t = TimeWindow
  .withTimeZone("PDT")
  .withOffset("2016-06-26 01:23:45-0700");

TimeWindow w = t.parse("-7d");

ZonedDateTime start = w.getStart();
ZonedDateTime end = w.getEnd();

READMEからの抜粋ですが、以下のような感じで簡潔かつ柔軟な表記が可能です(結果は現在時刻を2016-06-26 01:23:45-0700に設定した場合の例です)。

Duration Definition start end (exclusive)
0h this hour 2016-06-26 01:00:00-0700 2016-06-26 02:00:00-0700
0d today 2016-06-26 00:00:00-0700 2016-06-27 00:00:00-0700
0M this month 2016-06-01 00:00:00-0700 2016-07-01 00:00:00-0700
-1h last hour 2016-06-26 00:00:00-0700 2016-06-26 01:00:00-0700
-1h/now last hour to now 2016-06-26 00:00:00-0700 2016-06-26 01:23:45-0700
-60m/2017-01-23 01:23:45 last 60 minutes to now 2017-01-23 00:23:00-0700 2017-01-23 01:23:45-0700
-1d last day 2016-06-25 00:00:00-0700 2016-06-26 00:00:00-0700
-7d last 7 days 2016-06-19 00:00:00-0700 2016-06-26 00:00:00-0700
-7d/now last 7 days to now 2016-06-10 00:00:00-0700 2016-06-26 01:23:45-0700
-3d/2017-04-07 last 3 days from a given offset 2017-04-04 00:00:00-0700 2017-04-07 00:00:00-0700
+7d next 7 days (including today) 2016-06-26 00:00:00-0700 2016-07-03 00:00:00-0700
+7d/now next 7 days from now 2016-06-26 01:23:45-0700 2016-07-03 00:00:00-0700
-1w last week 2016-06-13 00:00:00-0700 2016-06-20 00:00:00-0700
-1M last month 2016-05-01 00:00:00-0700 2016-06-01 00:00:00-0700
-1y last year 2015-01-01 00:00:00-0700 2016-01-01 00:00:00-0700
-1h/2017-01-23 01:00:00 last hour to a given offset 2017-01-23 00:00:00-0700 2017-01-23 01:00:00-0700
-1h/2017-01-23 01:23:45 last hour to a given offset 2017-01-23 00:00:00-0700 2017-01-23 01:23:45-0700