読者です 読者をやめる 読者になる 読者になる

Akka Typedを試してみる

Scala

MEAPが開始してから丸4年、Akka in Actionがようやく完成したそうです。

完成版のPDFをパラ見していたところ、最後の16章にこれから入る予定の新機能の解説があり、その中でAkka Typedが取り上げられていたので少し試してみました。

Akka TypedはAkkaのアクターが返すメッセージを型安全に扱うためのものです。

まずはAkka Typedを使わずに普通にアクターを実装してみます。以下は受信したメッセージに反応して送信元にメッセージを返却する簡単なアクターです。

import akka.actor._
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

object Hello {
  final case class HelloMessage(name: String)
  final case class HelloResult(message: String)

  class HelloActor extends Actor {
    def receive = {
      case HelloMessage(name) => sender ! HelloResult(s"Hello $name!")
    }
  }
}

implicit val timeout = Timeout(5 seconds)

val system = ActorSystem("system")
val actor = system.actorOf(Props[Hello.HelloActor])
val f: Future[Any] = actor ? Hello.HelloMessage("Naoki")

f.mapTo[Hello.HelloResult].foreach { result =>
  println(result.message)
}

アクターに?で問い合わせた結果の型がFuture[Any]になっており、mapToで返却されるメッセージの型にキャストしています。この部分を型安全にしようというのがAkka Typedです。

同じ処理をAkka Typedで書き直してみます。

import akka.typed._
import akka.typed.ScalaDSL._
import akka.typed.AskPattern._
import akka.util.Timeout
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

object TypedHello {
  final case class HelloMessage(name: String, replyTo: ActorRef[HelloResult])
  final case class HelloResult(message: String)

  val helloBahavior = ContextAware[HelloMessage] { ctx =>
    Static {
      case HelloMessage(name, replyTo) => replyTo ! HelloResult(s"Hello $name!")
    }
  }
}

implicit val timeout = Timeout(5 seconds)

val system: ActorSystem[TypedHello.HelloMessage] = ActorSystem("system", Props(TypedHello.helloBahavior))
val f: Future[Hello.HelloResult] = system ? (TypedHello.HelloMessage("Naoki", _: ActorRef[TypedHello.HelloResult]))

f.foreach { result =>
  println(result.message)
}

ちょっとわかりにくいんですが、アクターの中で行っていた処理はContextAwareの中で行うようになっており、送信メッセージに返信先の型付きActorRefを持たせ、その返信先アクターに対してメッセージを送信するようになっています。ActorSystemActorRef?メソッドなどはAkka標準のものではなくAkka Typedが提供するものを使用することで型安全になるようになっています。

個人的には効果の割に修正が多くてちょっと厳しいかなという気がします。Akka in Actionにも「型安全のメリットは大きいので将来的にAkka Typedが重要なモジュールになる可能性はあるけど、今後大きく変更する可能性があるからまだプロダクションで使うべきではない」というようなことが書かれていました。

Akka in Action

Akka in Action

16章にはAkka Typedの他にAkka Streams、Akka HTTP、Akka Distributed Dataがちょっとずつですが紹介されています。

しかしこの本、完成するまで4年かかったのか…。4年…。

Scalaでコンパイル時にSQLをバリデーションするマクロを作ってみた

Scala

以前からscala-jdbcというScala用のシンプルなJDBCラッパーを作っています。

github.com

このscala-jdbcコンパイル時にSQLのバリデーションを行うためのマクロを追加してみました。

たとえば以下のように少し間違ったSQLカラム名の後ろに余計なカンマが付いている)を記述したとします。

sqlc("SELECT USER_ID, FROM USER")

するとコンパイル時に以下のようなエラーが報告されます。

[error] /Users/takezoe/gitwork/scala-jdbc/src/test/scala/com/github/takezoe/scala/jdbc/validation/SqlValidationSpec.scala:30: Encountered " <S_IDENTIFIER> "USER "" at line 1, column 40.
[error] Was expecting one of:
...
[error]     
[error]     sqlc("SELECT USER_NAME, FROM USER")
[error]         ^

デフォルトではSQLシンタックスのみがチェックされますが(SQLのパースにはJSqlParserを使っています)、コンパイル時のカレントディレクトリにschema.jsonというファイルを作っておくことでテーブルやカラムの存在チェックを行うこともできます。schema.jsonは以下のような感じです。

{
  "tables":[
    {
      "name": "USER",
      "columns": [
        { "name": "USER_ID" },
        { "name": "USER_NAME" },
        { "name": "COMPANY_ID" }
      ]
    },
    {
      "name": "COMPANY",
      "columns": [
        { "name": "COMPANY_ID" },
        { "name": "COMPANY_NAME" }
      ]
    }
  ]
}

なお、このマクロはまだ実験的な機能なのでscala-jdbcのSNAPSHOTバージョンでのみ提供されています。build.sbtには以下の依存関係を追加します。

resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"

libraryDependencies += "com.github.takezoe" %% "scala-jdbc" % "1.0.3-SNAPSHOT"

もし不正なバリデーション結果に遭遇した場合はGitHubIssuesでレポートをいただければと思います。

Scala関西 Summit 2016に参加します

Scala

来る10月8日(土)に大阪で開催されるScala関西 Summit 2016ですが、弊社もスポンサーとしてサポートさせていただいています。

summit.scala-kansai.org

スポンサーセッションでは「Play2+SlickだけじゃないScalaのWeb/DBフレームワーク事情」という発表をさせていただきます。

Scalaフレームワークデファクトスタンダードと言えばPlay + Slickですが、どちらも使いにくい部分や、手放しで採用できない問題があり、他のフレームワークへの移行も視野に入れています。このセッションではPlayおよびSlickの抱える問題点と、その代替となり得るフレームワークについてお話しさせていただきたいと考えています。

また、スポンサーブースではScalaで開発しているオープンソースプロダクトの展示なども行う予定ですのでお気軽にお声がけいただければと思います!

この際なのでScala逆引きレシピやScalaパズルもよろしくお願いします!!

Scala逆引きレシピ

Scala逆引きレシピ

Scalaパズル 36の罠から学ぶベストプラクティス

Scalaパズル 36の罠から学ぶベストプラクティス

OracleのDavid BuckさんによるBytecode Verification

Study

毎週金曜日に開催している社内勉強会ですが、今週は久々の外部ゲストとしてOracleのDavid Buckさんにお越しいただきBytecode Verificationについてお話しいただきました。

f:id:takezoe:20160903121051j:plain

Buckさんは今年もサンフランシスコで開催されるJavaOne 2016で3本のセッションに登壇されるそうで、そのうちの1セッションの内容を、しかも日本語でお話しいただけるという大変貴重な機会でした。

Bytecode Verificationが必要な理由

Bytecode VerificationはJavaVMが不正なバイトコードを検出するための機能ですが、普段その存在を意識することがほとんどないのではないかと思います。しかし実際のところ、以下のように不正なバイトコードに遭遇し得るシチュエーションには様々なものがあります。

Javaは古いコンパイラコンパイルされたクラスファイルでも動作させることができるのですが、バイトコードを操作するツールやライブラリがバイトコードに仕様の相違を理解せずに操作してしまいバイトコードが壊れてしまうというケースもあるのだそうです。

結論

結論としては非常にシンプルで「Bytecode Verificationはデフォルトで有効なので無効にしないでください」とのこと。

パフォーマンス面の理由からBytecode Verificationを無効にしているオープンソースプロジェクトもありますが、現代のコンピュータではほぼ無意味なので、余計なトラブルを防ぐためにも開発環境であろうとプロダクション環境であろうとBytecode Verificationを無効にするべきではないとのことでした。

壊れたバイトコードを読み込んだ場合、Bytecode Verificationが有効であればわかりやすいエラーメッセージが出るのですが、無効になっているとVMがクラッシュしたりします。こういった場合はコアダンプを見てもクラスファイルが壊れているのかVMのバグなのかの切り分けが非常に難しく、仕事柄いろいろとご苦労されてきた苦悩がありありと伝わってきましたw

日頃Javaを使っていてもJavaVMの中身まで触れる機会は中々ないので大変勉強になりました。マニアックな話題だったとは思うのですが、学びあり、笑いありの楽しい発表でしたので、JavaOne 2016に参加される方は是非Buckさんのセッションに足を運んでみていただければと思います。

豆知識

ちなみにデフォルトでは-Xverify:remoteが有効なのですが、以下のオプションを指定できます。

デフォルトがremoteなのは元々JavaはAppletでの利用が想定されており、アプリケーションのクラスファイルはリモートサーバから読み込んで実行することが前提だった時代の名残なのだそうです。Javaの歴史を感じますね〜。

Twitter製の大規模ソフトウェア向けビルドツール「Pants」を試してみる

PantsはTwitter社が開発した大規模ソフトウェア向けのビルドツールです。今年の5月に1.0がリリースされました。

github.com

主にPythonで書かれているようですが、JavaScalaPythonGolangのビルドに対応しており、Thriftのコード生成やMarkdownからのドキュメント生成などもサポートしているようです。

同種の大規模向けビルドツールにはGoogle製のBazelやFacebook製のBuckがあります。

github.com

github.com

いずれも元は社内用に開発されたツールがOSS化されたものですが、GoogleのBazelは2009年頃からGoogle社内で使われておりかなり歴史のあるツールのようです。

BuckやPantsはBazelを参考にしているようですが*1、BuckはAndroidアプリのビルドが高速だったり、Pantsは複数言語への対応やThriftのサポートなど各社のカラーが色濃く現れています。

大規模ソフトウェアのビルドの課題

そもそも大規模なソフトウェアのビルドにおける課題とは何なのでしょうか?大きく整理すると以下の2点が問題となるようです。

  • バージョン管理の煩雑さ
  • ビルドに時間がかかる

ソフトウェアの規模が大きくなるほどビルドに時間がかかるのは直感的に理解できると思いますが、バージョン管理の煩雑さというのはどういうことなのでしょうか。

大規模なソフトウェアでは機能単位でモジュール化したり、共通部分をライブラリ化するなどして開発の効率化を図りますが、ソフトウェアが大きくなればなるほどモジュールが増えていきます。それぞれのモジュールが独自にバージョンアップしていくと、同じライブラリを使っているモジュールでもモノによって異なるバージョンを使用していたりといった事態は普通に発生します。

そうすると最終的に複数のモジュールをまとめて1つの巨大なモジュールをビルドする際にライブラリのコンフリクトが発生してしまいます。規模が多くなればなるほどこのようなライブラリの整合性を取る作業は難しくなっていきます。

現在はGitHubなどの流行もありモジュール毎にリポジトリを作成する文化が一般的ですが、大規模なソフトウェアではこの問題を避けるため単一のリポジトリで全てのモジュールを管理するMono Repositoryが適しているとされています。Blaze、BuckそしてPantsといった大規模ソフトウェア向けのビルドツールはすべてこのMono Repositoryを前提としています。

Pantsを使ってみる

Pantsのインストールは簡単で、リポジトリのルートディレクトリで以下のコマンドを実行します。*2

$ curl -L -O https://pantsbuild.github.io/setup/pants && chmod +x pants && touch pants.ini

pantsコマンドが使用できるようになり、初回実行時にPythonや必要なライブラリなどが物凄い勢いでインストールされます。

$ ./pants -V
New python executable in /Users/naoki.takezoe/.cache/pants/setup/bootstrap-Darwin-x86_64/pants.ZQsYCJ/install/bin/python2.7
Also creating executable in /Users/naoki.takezoe/.cache/pants/setup/bootstrap-Darwin-x86_64/pants.ZQsYCJ/install/bin/python
Installing setuptools, pip, wheel...done.
Collecting pantsbuild.pants
  Downloading pantsbuild.pants-1.1.0.tar.gz (874kB)
    100% |████████████████████████████████| 880kB 1.4MB/s
...
Successfully installed Markdown-2.1.1 Pygments-1.4 ansicolors-1.0.2 cov-core-1.15.0 coverage-3.7.1 docutils-0.12 fasteners-0.14.1 futures-3.0.5 lmdb-0.89 monotonic-1.2 pantsbuild.pants-1.1.0 pathspec-0.3.4 pex-1.1.10 psutil-4.3.0 py-1.4.31 pystache-0.5.3 pytest-2.6.4 pytest-cov-1.8.1 pywatchman-1.3.0 requests-2.5.3 scandir-1.2 setproctitle-1.1.10 setuptools-5.4.1 six-1.10.0 twitter.common.collections-0.3.7 twitter.common.confluence-0.3.7 twitter.common.dirutil-0.3.7 twitter.common.lang-0.3.7 twitter.common.log-0.3.7 twitter.common.options-0.3.7
1.1.0

ビルドの設定はBUILDという名前のファイルに記述します。以下のような簡単な構成で試してみます。

reporoot/
 |
 +- java-sample/
     |
     +-HelloWorld.java
     |
     +-BUILD

BUILDファイルの内容は以下のような感じです。ソースディレクトリやパッケージがネストしている場合はglobsのパターンの書き方や、パターンを複数列挙することで対応できます。

java_library(
  name = 'java-sample',
  dependencies = [
  ],
  sources = globs('*.java'),
)

ビルドしてみます。

$ ./pants compile java-sample
...
11:08:37 00:02         [zinc]
                       [info] Compiling 1 Java source to /Users/naoki.takezoe/pants/.pants.d/compile/zinc/252d64521cf9/java-sample.java-sample/current/classes...
                       [info] Compile success at 2016/09/01 11:08:38 [0.072s]
...
11:08:38 00:03   [complete]
               SUCCESS

外部のライブラリを使う場合、別途ライブラリを定義する必要があります。

reporoot/
 |
 +- java-sample/
 |   |
 |   +-HelloWorld.java
 |   |
 |   +-BUILD
 |
 +- 3rdparty/
     |
     +-BUILD

3rdparty/BUILDはこんな感じです。java_libraryを列挙することで複数のライブラリを1つのBUILDファイルで定義することができます。

java_library(
  name = 'junit',
  jars = [
    jar('junit', 'junit', '4.12')
  ],
  scope = 'forced',
)

java-sample/BUILDでは以下のようにdependeniesを記述することでライブラリを使用することができます。

java_library(
  name = 'java-sample',
  dependencies = [
    '3rdparty:junit',
  ],
  sources = globs('*.java'),
)

他のプロジェクトを参照する場合も基本的には同じような感じです。以下のような構造のリポジトリで、java-sampleプロジェクトがjava-commonプロジェクトに依存しているとします。

reporoot/
 |
 +-java-sample/
 |  |
 |  +-BUILD
 |  |
 |  +-HelloWorld.java
 |
 +-java-common/
    |
    +-BUILD
    |
    +-StringUtils.java

java-sample/BUILDでは以下のようにBUILDファイルが存在するディレクトリを指定することで対象のプロジェクトを依存関係に追加することができます。

java_library(
  name = 'java-sample',
  dependencies = [
    'java-common',
  ],
  sources = globs('*.java'),
)

BUILDファイルはプロジェクト毎だけでなくディレクトリ毎に作ることができるのでパッケージ単位でBUILDファイルを作成することでビルド単位を細かく分割することができます。また、1つのBUILDファイルで複数のjava_libraryを定義することもできるので同一のディレクトリでも条件によってビルド単位を分けることが可能です。

まとめ

既存のビルドツールとPantsを比較した場合の大きな違いはプロジェクト毎ではなく、ディレクトリ毎など自由な単位でビルドファイルを配置できるということです。「ビルド単位を細分化することでビルドの並列可能性や、キャッシュの有効性を高める」というのが基本的なコンセプトのようです。

他のビルドツールと併用することもできるので*3、デプロイ用のビルドには既存のビルドツールを使用し、ローカルでの開発時や実行頻度の高いプルリクエストのビルドのみPantsで高速化する、というような使い方もありかもしれません。

*1:Buckは元Google社員が作ったそうで、ビルドファイルの記法もBazelそっくりらしいです。

*2:PantsはMono Repository用のビルドツールということもあり、リポジトリのルートディレクトリにインストールして使用することが想定されているようです。

*3:既存のビルドが特定のビルドツール向けのプラグインなどに依存してると厳しいですが…。

GitBucket 4.4をリリースしました

GitBucket

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

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

SQLダンプファイルのインポート

これまでデータベースのダンプはXMLSQLで行うことができましたが、インポートはXMLのみのサポートでした。4.4ではSQLでのインポートがサポートされ、代わりにXMLでのインポート・エクスポートが廃止されています。

f:id:takezoe:20160828120344p:plain

プライベートリポジトリでもgo getをサポート

4.3まではpublicリポジトリに対してのみgo getのサポートが有効でしたが、4.4からはprivateリポジトリに対しても有効になりました。GitBucketはprivateリポジトリに対して認証のないHTTPアクセスが送信された場合、以下のようなHTMLを返します。

<!DOCTYPE html>
<html>
  <head>
    <meta name="go-import" content="localhost:8080/root/hogege git http://localhost:8080/git/root/hogege.git" />
  </head>
</html>

マイルストーンを期限でソートして表示

マイルストーンを期限の昇順にソートして表示するようになりました。

f:id:takezoe:20160828120613p:plain

f:id:takezoe:20160828120621p:plain

apache-sshdを1.2.0にバージョンアップ

apache-sshdを1.2.0にバージョンアップしました。

DSA(ssh-dss)は脆弱性により推奨されておらずOpen SSHでも現在はデフォルトで無効になっています。これまでGitBucketはDSAしかサポートしていませんですたがapache-sshdのバージョンアップによってRSAも利用できるようになりました。

この他にも様々な改善やバグフィックスを行っています。詳細についてはIssueの一覧をご覧ください。

ThinkPad USBキーボードをちょっとだけ使いやすくする方法

PC

ThinkPad USBキーボードの現行モデルはThinkPad本体と同じくアイソレーションキーボードでパームレストがない分サイズもコンパクトです。Bluetoothモデルもあるのでタブレットと組み合わせて使うのもよいかもしれません。

しかし、先代のThinkPadキーボードと比べるとキーストロークが浅く、タッチはいまいちですし、トラックポイントの操作もやや窮屈な感じがします。キータッチはどうしようもないですが、トラックポイントについては改善の余地があります。

最近の薄型ThinkPadはディスプレイを閉じた際にトラックポイントキャップがディスプレイ表面に接触しないよう、ロープロファイルという背の低いトラックポイントキャップを使用しており、ThinkPad USBキーボードの現行モデルにも標準ではこのトラックポイントキャップが付属しています。先代モデルと比べると頭一つ分ほど高さが違います。

f:id:takezoe:20160825014930p:plain

そこで現行モデルのThinkPad USBキーボードのトラックポイントキャップを通常の高さのものと交換してみたところ、高さが増えた分小さい力でポインタを操作することができ、より繊細な操作を行うことができるようになりました。

トラックポイントのキャップはレノボのオンラインショッピングサイトの他、Amazonでも入手することができます。高さのあるキャップは3種類が2つずつ入ったセットの他、単種類のセットも販売されているようです。Amazonではノーブランドの互換品も売られているようで、使用感などがだいぶ異なるようなので注意してください。

Lenovo ThinkPlus トラックポイント・キャップ・コレクション [73P2698]

Lenovo ThinkPlus トラックポイント・キャップ・コレクション [73P2698]

ちなみにThinkPad本体やThinkPad USBキーボードに標準でついてくるロープロファイルのキャップは以下になります。現在の使い勝手に不満はないという方でもトラックポイントのキャップは割とすぐにヘタってグリップ力が落ちてしまうので定期的に交換するといいと思います。