Hatena::ブログ(Diary)

NetPenguinの日記 このページをアンテナに追加 RSSフィード

'アニヲタ' 兼 Perfume ファン 兼 'Scala, C/C++, Java プログラマ'
日々の愚痴をつづる日記です。
FTPソフトの NetPenguin とは何の関係もありません。

2015-02-04

[][][] Spring Boot で 例外ハンドリング 09:12  Spring Boot で 例外ハンドリングを含むブックマーク  Spring Boot で 例外ハンドリングのブックマークコメント

Spring Boot での例外ハンドリング方法について調べたので、そのメモです。

*1

実際に試していないので、問題なく動作するのかはわからないですが、 http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc の内容でほぼ足りそうです。

例外に直接 HTTP Status を紐付けてしまう (Using HTTP Status Codes)

例外クラスに @ResponseStatus をつけることで、Spring MVC の方で処理してくれるっぽいです。

@ResponseStatus(value = NOT_FOUND, reason = "Resource is not found.")
public class ResourceNotFoundException extends RuntimeException {
    ...
}

しかし、これだとエラーレスポンスのボディに JSON で詳細情報を返すのはできなさそう……


Controller クラスごとに例外ハンドリング (Controller Based Exception Handling)

いままで Spring MVC普通にやっていたのと同じように、Controller クラス内に @ExceptionHandler のついた例外ハンドリングメソッドを用意するだけです。

@Controller
public class FooController {
    // こんな感じ
    @ExceptionHandler
    @ResponseStatus(NOT_FOUND)
    public void notFound(ResourceNotFoundException ex) {
        // N/A
    }
}

@ResponseBody もつければ、JSON 形式で詳細情報を返すことはできるはず。

しかし、この方法システム全体で共通のエラーハンドリングには向かない。

抽象クラスつくるのはおすすめしない。というか、いまどき共通の機能抽象クラスにまとめるとか、ありえんでしょう……

(mix-in ならアリかも?java8ならインタフェースデフォルト実装機能を使うことで mix-in のようなことができるので、もしかしたらそれで出来るかも?)

システム全体での例外ハンドリング (Global Exception Handling)

@ControllerAdvice *2をつけたクラスを用意することで、すべての @Controller クラスに対する共通の設定を記述できる。

いってみたら、AspectJ実装を Weaving するのに似ている。

@ControllerAdvice のクラスでは @ExceptionHandler, @InitBinder, @ModelAttribute アノテーションをつけた三種類のメソッド定義できるらしい。

@ExceptionHandler だけでなく、@InitBinder も使えるのは嬉しい。空文字だったら null にしてしまうとか、そういうことが一箇所で済む。

@ControllerAdvice
public class FooAdvice {
    @ExceptionHandler
    @RespnseStatus(NOT_FOUND) 
    public void notFound(ResourceNotFoundException ex) {
        // N/A
    }
}

これで、Controller の抽象クラスを作るというアンチパターンから脱出できる……(といいな)

*1:たぶん、Spring Boot 関係ない。Spring MVC での話ですね……

*2Spring 3.2 からサポートしていたらしい。 http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html

トラックバック - http://d.hatena.ne.jp/NetPenguin/20150204

2014-01-21

[] mixi-inc/AndroidTraining で Android アプリ開発の学習00:15  mixi-inc/AndroidTraining で Android アプリ開発の学習中を含むブックマーク  mixi-inc/AndroidTraining で Android アプリ開発の学習中のブックマークコメント

スマートフォンアプリ*1を作れるようになっておきたいと思い、GitHub にある mixi-inc/AndroidTraining学習することにしました。

開発環境

AndroidStudio

AndroidTraining では EclipseADT を入れていますが、私は IntelliJベースとなっている Android Studio を利用していますIntelliJ カッコ良いんで。

Android Studiohttp://developer.android.com/sdk/installing/studio.html あたりからダウンロードしました。

ダウンロードしたバージョンは 0.3.2 だったのですが、起動時に最新版があるよ!と通知されるので即更新して 0.4.2 になっています

Android Studio を起動したら、まず最初SDKダウンロードしました。ツールバーにある SDK Manager のアイコンクリックして、SDK Manager から必要そうなものにチェックを入れて Install Package しています。おそらく初回では、最初からいくつかのパッケージが選択・削除されている状態だと思うので、そのまま Install Package ボタンクリックしたような記憶があります

Genymotion

Android の高速エミュレータです。http://www.genymotion.com/ から入手できます。要アカウント登録。

Genymotion は VirtualBox 上で動作するので、VirtualBox必要です。

http://dev.classmethod.jp/smartphone/android/genymotion-install/ あたりを参考にインストールしました。

SDK にも標準でエミュレータがついているのですが、Intel x86 Emulator Accelerator (HAXM) を有効にしてもちょっと遅い感じがあります。Genymotion は起動も描画も十分に早く、またエミュレータ自体のウィンドサイズに合わせて拡大/縮小される、GPSやカメラなどの偽装も簡単にできるのが良いです。

Genymotion Plugin

AndroidStudio でビルドしたアプリケーションデプロイ先として Genymotion を選択できるようにするために必要(と思う)なので入れておきます

AndroidStudion の Preferences -> Plugin で Browse Repositories ボタンクリックするとプラグインの一覧が表示されます。その中から Genymotion プラグインを選択して Download and Install します。


Tips

AndroidStudio を使っていて、なんだかわからないけどおかしいなと思った時にしている事のメモです。

レイアウトファイル記述している FQCN が赤くなっている*2

メニューから File -> Invalidate Caches / Restart... をクリックし、表示されたダイアログの Invalidate and Restart をクリックしてキャッシュクリアおよび再起動で解消しました。


依存ライブラリを追加したい

依存ライブラリを利用するモジュールの build.gradle に dependencies ブロックを追加します。

例えば twitter4j-stream を追加するならば以下のようになります

dependencies {
    compile group: 'org.twitter4j', name: 'twitter4j-stream', version: '3.0.5'
}

Gradle についてはユーザガイド日本語訳されたありがたい方いるので、それを参考にすると良いかと思います

ユーザガイド日本語訳版は http://gradle.monochromeroad.com/docs/userguide/userguide.html になります


依存ライブラリの追加が反映されない?

メニューから Tools -> Android -> Sync Project with Gradle Files をクリックします。ツールバーにもあるのでそちらでもOKです。

コード補完したときに候補に出てくるようになります


依存ライブラリ追加したら、ビルドに失敗するようになった

ビルドに失敗し、"META-INF/LICENSE.txt" が重複しているよ!みたいなメッセージが出ている状況のことです。

build.gradleandroid の packagingOptions ブロックに exclude に上記のファイル名を指定します。

android {
  ...
  packagingOptions {
    exclude 'META-INF/LICENSE.txt'
  }
}

*1:私の周りでは、よく「クライアントアプリ」と呼称されている気がするが、Windows 上などで動作するアプリ区別がつかなくて違和感を感じる

*2:AndroidStudio側でクラスをうまく認識できていないみたいですね。ビルドGradleがやるのでできました。

トラックバック - http://d.hatena.ne.jp/NetPenguin/20140121

2013-12-18

[] Dispatch を使ってみる 00:06  Dispatch を使ってみるを含むブックマーク  Dispatch を使ってみるのブックマークコメント

このエントリScala Advent Calendar の 18 日目になります。

Dispatch というライブラリを使ってみます。

Dispatch とは

Dispatch というのは、Scala 用 の HTTP クライアントライブラリです。Dispatch には classic と reboot という二種類があり、前者は古い方、後者は一から書き直された新しい方っぽいです。

そして、「Dispatch scala」でみつかる http://dispatch.databinder.net/Dispatch.htmlreboot の方です。でも、github で 単に dispatch となっているのは classic で、reboot の方は dispatch/reboot です。ドキュメント上は無印rebootgithub上は無印=classicという感じでなんだか混乱します。

今回使用するのは新しい方の Dispatch (つまり reboot の方) です。以降は reboot の方についての記述になります。

Dispatch では実際の HTTP 周りの処理を async-http-client に任せていて、Dispatch 自体はこのライブラリに対する薄いラッパーのような感じになっています。そのため実際に使用する場合、async-http-client の方を調べる必要があるかもしれません。

準備

Dispatch を利用できるように、以下を記述した build.sbt ファイル*1作成します。

依存ライブラリに dispatch-core を指定しているだけです。また、JSON を扱えるように dispatch-json4s-native も追加しておきます。

libraryDependencies ++= Seq(
  "net.databinder.dispatch" %% "dispatch-core" % "0.11.0",
  "net.databinder.dispatch" %% "dispatch-json4s-native" % "0.11.0"
)

sbt の console 上で試す

sbt の console を起動して実際に Dispatch を使用して GET や POST をしてみたいと思います。

依存ライブラリダウンロードするため、初回の起動はちょっと時間がかかるかもしれません。

$ sbt console
[info] Set current project to root-2013-advent-calendar (in build file:/C:/development/workspaces/sandbox/scala/2013-advent-calendar/)
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.10.2 (Java HotSpot(TM) Client VM, Java 1.6.0_23).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import dispatch._, Defaults._
import dispatch._
import Defaults._

scala> 

dispatch パッケージ直下に主要な部品定義されているので、 import dispatch._ で利用できるようにします。

また、 dispatch.Defaults には Future の実行で必要な暗黙のパラメータに渡す値が定義されているようです(defaults.scala)。

GET

GET リクエストを発行してみます。

scala> Http(url("https://api.github.com/gists?per_page=1") OK as.String)
res1: dispatch.Future[String] = scala.concurrent.impl.Promise$DefaultPromise@1e9ed79

scala> res1()
res2: String = [{"url":"https://api.github.com/gists/7995189","forks_url":"http
s://api.github.com/gists/7995189/forks","commits_url":"https://api.github.com/g
~~(snip)~~
2-16T21:59:24Z","description":"","comments":0,"user":{"login":"thion","id":3336
77,"avatar_url":"https://gravatar.com/avatar/3d082acb2ebd10448ee5cf790cdc3c96?d
=https...

scala>

url("...") の部分でリクエストを構築しています。上記の例では URL文字列で指定していますが、ホスト名、パスクエリパラメータを別個に指定することも可能です。request.scalaリクエストの構築に使用する部品定義されています。

リクエストの結果となるレスポンスをどのように扱うかを OK as.String の部分で指定しています。レスポンスが成功(2xx)の場合のみ処理して、レスポンスボディを文字列として取り出すレスポンスハンドラを指定しています。OKhandlers.scala に、as.Stringas/core.scala定義があります。

Http(...) で構築したリクエストを実行し、その結果をレスポンスハンドラで処理しています。実際には Http(...)戻り値Future インスタンスのため、すぐにレスポンスが得られているわけではありません。res1()*2することで、レスポンスが取得されるまでブロックして、その値を取り出しています。*3


以下は、リクエスト構築の際に host 等を使用する場合の例になります。また、as.json4s.Json を指定することでレスポンスを文字列としてではなく JSON として取り出しています。*4

scala> Http(host("api.github.com").secure / "gists" <<? Map("per_page" -> "1") OK as.json4s.Json)
res3: dispatch.Future[org.json4s.JValue] = scala.concurrent.impl.Promise$DefaultPromise@abedf1

scala> import org.json4s._
scala> import org.json4s.native.JsonMethods._
scala> pretty(render(res3()))
res4: String =
[{
  "url":"https://api.github.com/gists/8004290",
  "forks_url":"https://api.github.com/gists/8004290/forks",
~~(snip)~~
  "comments":0,
  "user":{
   ...
scala>


POST

続いて POST リクエストを発行してみます。

基本的には GET場合と同様にリクエストを構築して実行するだけです。

scala> import org.json4s.native.Serialization, Serialization.{write}
import org.json4s.native.Serialization
import Serialization.write

scala> write(Map("public" -> true, "files" -> Map("file1.txt" -> Map("content" -> "String file contents"))))
res5: String = {"public":true,"files":{"file1.txt":{"content":"String file contents"}}}

scala> Http(host("api.github.com").secure / "gists" << res5 OK as.json4s.Json)
res6: dispatch.Future[org.json4s.JValue] = scala.concurrent.impl.Promise$DefaultPromise@2407b8

scala> pretty(render(res6()))
res7: String =
{
  "url":"https://api.github.com/gists/8005261",
  "forks_url":"https://api.github.com/gists/8005261/forks",
~~(snip)~~
  "description":null,
  "comments":0,
  ...
scala> 

<< を使用してリクエストボディに JSON 文字列を指定しているのが、GET の例との差になります。*5

リクエストに対してどのような操作が可能かは requests.scala定義されている RequestVerbs を継承している各種 trait を見るとわかるかと思います。

また、GET / POST 両方の例とも、GET なのか POST なのかを明示的に指定していません。これはリクエストデフォルトGET になっていて、またリクエストボディを指定した場合デフォルトが POST となるため、上記の例では明示的な指定が不要でした。host("...").GET のように記述することで明示できます。


以上、簡単にではありますが Dispatch を試すときの手がかりになれば幸いです。

おまけ

Verbs 引数 説明
HEAD, GET, POST, PUT, DELETE, PATCH, TRACE, OPTIONS - HTTP メソッドを指定
url - リクエストURL 文字列を取得
/ String *6 パスを追加
secure - https:// でアクセスする
<:< Traversable[(String, String)] ヘッダを指定
<< Traversable[(String, String)] リクエストボディにパラメータを指定
<< String リクエストボディに文字列を指定
<<< java.io.File ファイルアップロード
<<? Traversable[(String, String)] クエリパラメータを指定

*1:sbtを使用します

*2EnrichedFutureで Future に apply メソッドが追加されています

*3:Future の使い方については割愛させていただきます

*4:json4s については割愛

*5GET の例では <<? を使用してクエリパラメータを指定していました

*6:toString されるオーバーロードがあるので AnyVal でもOK

トラックバック - http://d.hatena.ne.jp/NetPenguin/20131218

2013-09-07

[][]NAS4Free 9.1 で HDD へのインストールに失敗する場合 00:16 NAS4Free 9.1 で HDD へのインストールに失敗する場合を含むブックマーク NAS4Free 9.1 で HDD へのインストールに失敗する場合のブックマークコメント

LivceCD からの起動で HDD へのインストールに失敗する場合対処

ML115G5 に最新版のNAS4Freeインストールしようとしたら、パーティション作成とかで失敗した。

以下によりインストールできた。


1. ブート途中でSafeモードとかを選択出来る画面が出ると思うので、ブートを一時停止して Prompt ? を表示するモードをえらびます。 9.1.0.1(revision 847) ですと選択肢の5.になってました。

2. 表示されたプロンプト(OK とか出ていた気がする)で以下のように入力して、ブートを続行。

set kern.geom.raid.enable=0
boot

3. HDDアクセスできた!

LiveCD の設定値がいまいち?なのかな。USB メモリインストールして起動であれば、HDD も問題なく読めるし。

トラックバック - http://d.hatena.ne.jp/NetPenguin/20130907

2012-06-24

[][] Windows で nslookup で IP アドレスは引けるけど、ping は通らない場合対処 17:05  Windows で nslookup で IP アドレスは引けるけど、ping は通らない場合の対処を含むブックマーク  Windows で nslookup で IP アドレスは引けるけど、ping は通らない場合の対処のブックマークコメント

DNSClient サービスDNS 情報キャッシュしているために発生していることが多い。

ipconfig /flushdns では効果が無い場合は、DNSClient サービス再起動するとなおる。

というか、別にキャシュしなくてもいいや・・・ということで、DNSClient サービスを止めてみた。

今のところ、問題なく動いているっぽい。

[][] fstab で CIFS のファイルシステム記述しているけど、マウントされない場合 17:05  fstab で CIFS のファイルシステムを記述しているけど、マウントされない場合を含むブックマーク  fstab で CIFS のファイルシステムを記述しているけど、マウントされない場合のブックマークコメント

netfs サービスが起動していない可能性が高い。

"/sbin/chkconfig --list netfs" と打って、on となっているか確認してみる。

off になっている場合は "/sbin/chkconfig netfs on" と打って on にしておく。

これでもダメな場合は _netdev オプションを追記してみる。

NetPenguinNetPenguin 2012/06/24 17:08 一年ちょっとまえにも同様のメモを書いているな・・・同じ事でハマったのかorz

トラックバック - http://d.hatena.ne.jp/NetPenguin/20120624
2004 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2005 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 12 |
2008 | 01 | 03 | 04 | 06 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 04 | 11 | 12 |
2011 | 01 | 02 | 03 | 10 |
2012 | 06 |
2013 | 09 | 12 |
2014 | 01 |
2015 | 02 |
PV:473986