yan's Handicraft

作成したフリーソフトを公開
Home » scala » Archive by category 'Scalatra'

Scalatra でアクセスログとエラーログを出力したい

5月 22nd, 2013 Posted in PC/システム開発, scala, Scalatra

開発していると、必ずと言っていいぐらい、「アクセスログ」や「異常発生時のログ」の出力
を求められるので、Scalatra ではどうやったらいいのかやってみました。

  • org.scalatra.ScalatraServlet#serviceHttpServlet#service をオーバーライドして
    handle メソッドを呼び出す実装になっていたので、handle メソッドオーバーライドしてあげれば
    ログが統一的に出力でるのではないかと思いやってみました。

    • 結果
      • アクセスログは出力できるのですが、例外がキャッチできず、異常発生時のログが出力できませんでした。
        ソースを追いかけてみると、handle メソッド処理中に例外が発生すると、
        所々で例外をキャッチしていました。
  • 更にソースを読むと変数org.scalatra.ScalatraBase#errorHandler が、例外が発生時の
    処理に使われているのが分かりました。
    変数errorHandlerを書き換えてやると、異常時にログが出力できました。

    • 特記事項
      • errorHandler の型は type ErrorHandler = PartialFunction[Throwable, Any]
        という風に定義されています
      • Googleグループ scalatra-user でこの辺のことを議論されているスレッドがありました。==>
        “Logging exceptions”

実装例

テンプレートとして作成される ????Stack クラスに、以下のような感じで実装してみました

  errorHandler = {
    case rt: RuntimeException => {
      log.error("システムエラー発生 (RuntimeException) Path=:" + Option(request).fold("")(_.getRequestURI) +
        "\n  ErrorMessage=" + rt.getMessage, rt)
      status = HttpServletResponse.SC_BAD_REQUEST
      "Exception occured!"
    }

    case rt: Throwable => {
      log.error("システムエラー発生 (RuntimeException以外) :\n  ErrorMessage=" + rt.getMessage, rt)
      throw rt
    }
  }

  override def handle(req: HttpServletRequest, res: HttpServletResponse): Unit = {
    val path = req.getRequestURI
    val startMillis = System.currentTimeMillis()
    log.info( """[AccessLog] Path={}""", path)
    try {
      super.handle(req, res)
    } finally {
      val elapsedMillis = System.currentTimeMillis() - startMillis
      log.debug( """[EndLog]elapsedMillis={} ,Path={}""", elapsedMillis, path)
    }
  }

Scalatra のテスト

5月 21st, 2013 Posted in PC/システム開発, scala, Scalatra

Scalatraのテストを試してみました。
Scalatraの本家サイト”Scalatra guides” の Testing あたりに、 説明があるので、それをもとに試してみました。

2013-5-21時点では、Scalatest を使う方法と Specs2 を使う方法とが説明されています。

感想とメモ

  • APサーバを起動せずに、単体テストとして、サーブレットの動作確認が簡単にでき、お手軽。(Scalatest 、Specs2 両方とも)
    • org.scalatra.test.EmbeddedJettyContainer をいう trait をテストクラスが実装していて、 Jettyのコンテナ(のモックかな?)がサーブレットコンテナを提供してくれている模様。
    • 本家サイトのサンプルとほとんど変わりませんが、 書いてみたテストソースはyangiYAのGitHubのこのあたりにあります
  • 私の好みとしては、Scalatest でテストするタイプ。Specs2 のほうは、なんだか記述がまどろっこしいように感じてしまいます。 ( Specs2 のほうは、BDD(Behavior Driven Development , (振る舞い駆動)) で最近の流行だとは思います。 )
  • Scalatest のほうは、依存を追加しないとコンパイルできませんでした。
    project/build.scala に以下を追加してから、sbt で gen-ideaeclipse を実行しました。
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
  • テストクラスが実装している org.scalatra.test.Client のおかげで、 テストクラスから、HTTPアクセス結果の「HTTPステータス」「HTTPレスポンス」などにアクセスできるようになっています。 単体テストとして、HTTPの結果を確認するテストコードを書けるようになっている思想のようだ

以上、簡単な感想でした。
画面でいちいち動作確認せずに、簡単な疎通レベルのテストが 単体テストとしてかけそうでいいなと思います。(Rubyist にとっては、当然なのかな)

Windows環境で Linux ぽく Scalatra をセットアップする

5月 12th, 2013 Posted in sbt, scala, Scalatra

Windows環境でシェルを動かす最もポピュラーな方法は Cygwin だとおもいますが、 Cygwinはインストールも大げさで自力でセットアップする部分が多いと思います。
「GitHub for Windows」を使えばシェルが動くようになり、 gitもお手軽に利用できるので「GitHub for Windows」をつかって、 scaratra を セットアップしてみたいと思います。

事前準備


事前にインストールしておくもの

  • JDK1.7(Java SE7)
  • GitHub for Windows
    • GitHub for Windows の以下を設定しておいてください
      1. toolsoptions... を選ぶ
      2. default shellGit Bash をクリックして選択状態にする
      3. UPDATE ボタンをクリックして保存

Windowsの環境変数を設定しておくもの

windowsの環境変数 PATH に設定するもの

  • c:\Users\<ユーザID>\bin
  • <Jdkインストールディレクトリ>\bin

windowsのシンボリックリンクを作成する

これをやっておかないと、あとの作業でつまづきます。
(Git Shellが提供するコマンドでは windowsのパスとUnixぽいパスをうまくだましてくれるのですが、
javaに渡す引数はそのまま渡されてしまうようなので、シンボリックリンクで何とかしようという対応策です)

  1. コマンドプロンプトを管理者として起動します
  2. mklink /D c:\c c:\ を実行します。(cというディレクトが、cドライブ直下となるようにする)

Git Shell でLinuxぽく作業する


Conscript をインストール

以下のコマンドを実行します

cd ~;
curl https://raw.github.com/n8han/conscript/master/setup.sh | sh

動作確認

  • cs --version コマンドを実行して、色々ダウンロードされたあと、ヴァージョンと、Usage が表示されれば成功

giter8 をインストール

以下のコマンドを実行します

cs n8han/giter8
  • これでセットアップしてくれるのですが、 bin/g8.bat が作られて、 そのままでは、Git Shell でg8コマンドが実行できないです。
    仕方ないので、bin/g8.batを参考に bin/g8 ファイルを作成します。 (以下をテキストエディタで作成)
#!/bin/sh
java ${CONSCRIPT_OPTS} -Xmx1G -jar "${HOME}/.conscript/sbt-launch.jar" "@file:///C:/Users/yanagawa.h/.conscript/n8han/giter8/g8/launchconfig" "$@"

動作確認

  • g8 コマンドを実行して、色々ダウンロードされたあと、ヴァージョンと、Usage が表示されれば成功

新しい scaratra プロジェクトを作成する

  • プロジェクトを作成するディレクトリを予め作成して、そのディレクトリに移動します。
    (たとえば以下のような感じで)
cd ~;
mkdir newPrj
cd newPrj
  • 以下を実行して新規プロジェクトを作成します
g8 scalatra/scalatra-sbt
  • 対話的に入力を求められるので、以下のような感じで入力していきます
organization [com.example]: jp.que.ti
package [com.example.app]: sample
name [My Scalatra Web App]: foo
scalatra_version [2.2.0]:
servlet_name [MyScalatraServlet]:
scala_version [2.10.0]: 2.10.1
version [0.1.0-SNAPSHOT]: 0.0.1-SNAPSHOT

Template applied in .\foo
  • name が作成されるディレクトリになります。 (参考:scaratra本家の説明 なんとなく直観で何を入れれば分かるかとは思いますが、詳しいことは本家サイトの説明を! )
  • これで、scaratra の新規プロジェクトが作成されました
  • 各種設定ファイルをバージョン管理システムに投入したくなければ、 このあたりでコミットするのが良いらしい。 ( 参考:3分でsbtとEclipseに対応したScalaプロジェクトを作る )

scaratra プロジェクトをビルドする

  • プロジェクトディレクトリで実行すれば完了
sbt

Webアプリケーションを起動する

container:start

動作確認

  • localhost:8080/ にアクセスして、 「Hello, world!」が表示されれば成功
  • ソースの変更をソースに反映させたい場合は、以下も実行
~ ;copy-resources;aux-compile
  • サーバーを止める場合は以下
container:stop

warファイルを作成する

  • sbt コマンド待ちの状態で、以下を実行します。(<プロジェクトディレクトリ>/target/scala-2.10 以下に作成されます )
package

Eclipse用の設定ファイルを作成する

  • plugins.sbt に sbteclipse-plugin の依存ライブラリを追記します
    cd <プロジェクト名> としてから (今回の例では cd foo) 以下を実行します。 ( 参考:本家のIDEサポートページ )
cat <<'EOF' >> project/plugins.sbt


addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0")
EOF

  • 最新バージョンは sbteclipse のサイト(GitHub) で確認してください
  • sbt コマンド待ちの状態で、以下を実行します。( .project@ と.classpath` ファイルができる)
eclipse

IntelliJ IDEA用の設定ファイルを作成する

  • plugins.sbt に sbt-idea プラグインの依存ライブラリを追記します
    cd <プロジェクト名> としてから (今回の例では cd foo) 以下を実行します。 ( 参考:本家のIDEサポートページ )
cat <<'EOF' >> project/plugins.sbt


addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.4.0")
EOF

gen-idea


追記

最初はうまくいっていた、scalatra の新規プロジェクト作成ですが、
エラーが発生するようになったしまいました。
(原因解明できていません。いろいろ環境いじっていたので、何がきっかけかもよくわからず。。。)
Windowsでsbtを使っていると、なぜかハマることが多いような気がします。

$ g8 scalatra/scalatra-sbt
java.lang.IllegalArgumentException: Invalid wildcards +refs/pull/*/head:refs/remotes/origin/pr/*
        at org.eclipse.jgit.transport.RefSpec.(RefSpec.java:142)
        at org.eclipse.jgit.transport.RemoteConfig.(RemoteConfig.java:176)
        at org.eclipse.jgit.api.CloneCommand.fetch(CloneCommand.java:151)
        at org.eclipse.jgit.api.CloneCommand.call(CloneCommand.java:121)
        at giter8.Apply$class.clone(apply.scala:38)
        at giter8.Giter8.clone(giter8.scala:3)
        at giter8.Apply$class.inspect(apply.scala:21)
        at giter8.Giter8.inspect(giter8.scala:3)
        at giter8.Giter8.ghInspect(giter8.scala:48)
        at giter8.Giter8.run(giter8.scala:24)
        at giter8.Giter8.run(giter8.scala:12)
        at giter8.Giter8.run(giter8.scala:3)
        at xsbt.boot.Launch$.run(Launch.scala:55)
        at xsbt.boot.Launch$$anonfun$explicit$1.apply(Launch.scala:45)
        at xsbt.boot.Launch$.launch(Launch.scala:69)
        at xsbt.boot.Launch$.apply(Launch.scala:16)
        at xsbt.boot.Boot$.runImpl(Boot.scala:31)
        at xsbt.boot.Boot$.main(Boot.scala:20)
        at xsbt.boot.Boot.main(Boot.scala)
Error during sbt execution: java.lang.IllegalArgumentException: Invalid wildcards +refs/pull/*/head:refs/remotes/origin/pr/*