eyecatch
Sun, Mar 5, 2017

Firedrop(プライベートベータ)が全く期待外れだった件

Firedropという現在開発中のサービスがある。 WebサイトのデザインをAIがサポートしてくれるサービスだ。 2016年夏のニュースを見たとき、AIがテキストコンテンツを解析してサイトを自動構成してくれ、さらにA/Bテストなどを自動でやってサイトを継続的に改善すると言う衝撃的なふれこみだったので、即座にアーリーアクセスに登録した。 それからしばらく忘れていたが、3月2日にプライベートベータへの招待メールが来たので早速試してみたら、かなりのスモールスタートをしたようで全く期待外れだった。 (adsbygoogle = window.adsbygoogle || []).push({}); Firedrop(プライベートベータ)の機能 Firedrop(プライベートベータ)では、SachaというAIがWebサイトの構築をサポートしてくれる。 こいつが実のところほとんど知性を感じない単なるチャットボットで、なるほどこれは見事な人工無脳だと感心してしまうほどだ。 Firedropのアカウントを作るとまず、Sachaとチャットしながらサイトの概要(タイトル、概要、画像など)を教えることになる。 するとSachaがざっくりとシングルページのサイトを作ってくれるので、それをまたSachaとのチャットで調整したりコンテンツ追加したりする。 チャットと言っても、基本はこちらは5,6個ある選択肢の中からセリフを選ぶサウンドノベル方式で、一応任意の文章も入力できるがあいさつするくらいしか使い道がない。 追加コンテンツはテキストと画像を渡すと自動でレイアウトしてくれるが、すごくいい感じにしてくれるというわけでもないし、むしろ画像が見切れたりするし、細かい調整はできないので、妥協できるレイアウトになるまでチェンジを繰り返すデリヘル方式を採ることになる。 デリヘルなんて利用したことないけど。 画像は自分でアップロードもできるけどFiredropが提供しているものもあって、後者のやつはSachaにキーワードを伝えるとそれっぽい画像を探してくれるあたりに唯一知性を感じる。 デザインができたらSachaに頼むとfiredrop.meドメインで公開してくれる。 (FiredropのUIのスクリーンショットを載せようかと思ったけど、プライベートベータの規約を読んだ感じだめそうだったので載せない。) 実際に作ってみた 今回実際にFiredropでGoslingsのサイトを作ってみて、できたのがこれ。 ひどい。 そもそも当初のテキストコンテンツを解析してサイトを自動構成というコンセプトはどこへ行ったのか。 GoslingsのReadmeを入力したらシャレオツなサイトをささっと作ってくれるイメージだったんだけど。 まだまだ開発中の機能がたくさんあるそうなので、GAまでにはもうちょっとなんとかなるんだろう。 あまり期待はしない。
eyecatch
Tue, Jan 24, 2017

Goslings開発メモ - その5: Spring Boot最終編 (静的リソース処理)

「Goslings開発メモ - その4: Spring Boot続続続編 (ロギング)」の続き。 Spring Boot最終編で、静的リソース処理について。 (adsbygoogle = window.adsbygoogle || []).push({}); Spring Boot(Spring MVC)での静的リソース処理 この時点でのGoslingsは単なるREST APIサーバで、アクセスしてもJSONを返すだけだ。 アプリとしての体を成すためには、そのAPIを利用するクライアントコード、つまりHTMLドキュメントやCSSファイルやJavaScriptファイル(静的リソース)も返すようにしないといけない。 HTMLドキュメントを返す場合、普通はなんらかのテンプレートエンジンを使うものだが、Goslingsは本当に単純なGUIなので、サーバに置いたHTMLファイルをそのまま返したい。 「Getting Started Guides」にはServing Web Content with Spring MVCというのが乗っているが、これはThymeleafというテンプレートエンジンを使うものなのでちょっと違う。 Spring Bootリファレンスガイドによると、クラスパス(またはServletContextのルート)の/static/、/public/、/resources/、/META-INF/resources/のいずれかに静的リソースを置けば、特にコードを書かなくてもクライアントからアクセスできるらしい。 (逆に、一般的に静的リソースを置く場所である、プロジェクトのsrc/main/webapp/には置くべきでないとのこと。これは、jarにパッケージングするときにビルドツールに無視されることが多いため。) この仕組みについて、この記事を参考にちょろっとソースを見た感じでは、これらのパスはResourcePropertiesのCLASSPATH_RESOURCE_LOCATIONSに定義されていて、これをWebMvcAutoConfigurationがResourceHandlerRegistryでリソースロケーションとして登録することで静的リソース置き場たらしめている模様。 (このResourceHandlerRegistryはResourceHttpRequestHandlerを設定するファサード的なものっぽい。) で、@SpringBootApplication(その1参照)が付いているクラスがあって、spring-webmvc.jarがクラスパスにあると、@EnableWebMvcがSpring Bootによって付けられ、そこからごにょごにょして上記WebMvcAutoConfigurationが実行される。 spring-webmvc.jarはspring-boot-starter-web.jar(その1参照)が引っ張ってくる。 なお、Spring MVCの静的リソース処理の全体の流れについては 、ちょっと古いけど「handling static web resources」という記事が分かりやすい。 要は、URLに指定されたパスからサーバ上のリソースを探し当てるResourceResolverというものが優先度順に連なっているリゾルバチェイン(ResourceResolverChain)があって、まずこいつがリソースを取得する。 次に、そのリソースを加工するトランスフォーマチェイン(ResourceTransformerChain)というものに通し、その結果をクライアントに返す。 トランスフォーマチェインはResourceTransformerが連なったもの。 リゾルバチェインとトランスフォーマチェインは上記ResourceHttpRequestHandlerに設定される。 リゾルバには以下の様なものがある。 PathResourceResolver: ResourceHttpRequestHandlerに設定されたリソースロケーションからリソースを単純に検索するリゾルバ。 CachingResourceResolver: キャッシュからリソースを検索するリゾルバ。テンプレートエンジンの処理結果のキャッシュとかが返るのは多分ここから。 GzipResourceResolver: gzipで圧縮されたリソース、つまりURLで指定されたパスに.gzという拡張子を付けたリソースを検索するリゾルバ。 VersionResourceResolver: リソースバージョニングを実現するためのリゾルバ。 WebJarsResourceResolver: WebJarsのjarファイル内のリソースを検索するリゾルバ。 リゾルバの設定などについてはQiitaのこの記事ががよくまとまっている。 凝ったことをしたいときは参照しよう。 トランスフォーマには以下の様なものがある。 CssLinkResourceTransformer: CSSファイル内のリンクをクライアントがアクセスできるURLに変換する。 CachingResourceTransformer: 変換したリソースをキャッシュする。 AppCacheManifestTransformer: HTML5のAppCacheマニフェスト内のリソースを扱うトランスフォーマ。 デフォルトでResourceHttpRequestHandlerにはPathResourceResolverだけが設定されている。 以上をまとめると、クライアントからGetリクエストが来ると、WebMvcAutoConfigurationが設定したリソースロケーション(e.g. /static/)をPathResourceResolverが検索して、そこに置いてあるHTMLファイルとかをクライアントに返してくれる、ということであろう。 Javaのコードを全く書かなくていいので楽。 Javaのコードを書いて静的リソースファイルを明示することもできる。 Qiitaの記事によれば、@Controllerを付けたクラスのリクエストハンドラで以下の様にファイルへのパスを返せばいいらしい。 @RequestMapping("/hoge") public String hoge() { return "/hoge.html"; } 単純な静的リソースに対してこれをやるユースケースはあまりなさそう。 テンプレートエンジンを使っていてパラメータを渡したいときにはこういうリクエストハンドラを書くことになる。 Spring Bootのウェルカムページとファビコン Spring Bootはindex.htmlとfavicon.icoという名のファイルを特別扱いする。 前者がウェルカムページで後者がファビコン。 ウェルカムページ Spring Bootのリファレンスガイドにもちらっとかいてあるけど、リソースロケーションにindex.htmlというファイルを置いておくと、それがウェルカムページとして設定され、URLのパスにルート(e.g.
eyecatch
Tue, Jan 17, 2017

Goslings開発メモ - その4: Spring Boot続続続編 (ロギング)

「Goslings開発メモ - その3: Spring Boot続続編 (例外処理)」の続き。 Spring Boot続続続編で、ロギングについて。 (adsbygoogle = window.adsbygoogle || []).push({}); Spring Bootアプリにおけるロギング Spring Bootアプリにおけるロギングについては公式のマニュアルとHow-toガイドを読むべし。 この記事にはこれらの内容をまとめておく。 Spring Bootは内部でのロギングにApacheのCommons Loggingを使っている。 Commons Loggingはファサードライブラリだ。 つまり、Commons LoggingはロギングAPIだけをアプリケーションに提供し、実際のログ出力処理をするロギング実装ライブラリへの橋渡しとして機能する。 ロギング実装ライブラリには色々な選択肢があるが、Spring BootはJUL、 Log4j 2、Logback用のデフォルト設定を備えているので、これらのいずれかを使うのが楽であろう。 全てのスターターはspring-boot-starter-loggingというロギングスターターに依存していて、これがLogbackを使うので、普通はそのままLogbackを使うことになる。 spring-boot-starter-loggingは、JUL、Commons Logging、Log4j、SLF4Jによるログ出力をLogbackにルーティングするため、アプリ側や他の依存ライブラリがこれらを使っていてもLogbackに一本化できる。 spring-boot-starter-loggingの代わりにspring-boot-starter-log4j2に依存し、Log4j 2を使う方法もあるが、Goslingsには普通にspring-boot-starter-loggingを使った。 また、Goslings本体のログ出力には、プレースホルダを使いたかったのでSLF4Jを使った。 Spring Bootアプリにおけるロギング設定 Spring Bootが備えているデフォルトのロギング設定は、ERROR、WARN、INFOレベルのログをいい感じにフォーマットしてコンソールに吐くというものになっている。 以下この設定の変更方法などを書く。 ファイルへのログ出力 ログをファイルにも吐くようにするには、logging.fileというプロパティでファイルパスを指定するか、logging.pathというプロパティでディレクトリパスを指定すればいい。 (後者の場合ログファイル名はspring.logになる。) Spring Bootアプリでプロパティを指定する方法は色々あり(こことかここ参照)、大抵はapplication.propertiesで指定するんだろうけど、手軽にコマンドラインで以下の様に指定することもできる。 java -jar build/libs/goslings-0.0.1.jar --logging.file=build/hoge.log ログファイルはデフォルトで10MBでローテーションする。 ログレベル ログレベルには重大度の低い方からTRACE、DEBUG、INFO、WARN、ERROR、FATALの6段階があり、指定したログレベル以上のログが出力される。(OFFというログ出力を止めるものもある。) つまりSpring BootのデフォルトのログレベルはINFOだということだ。(LogbackにはFATALがなくERRORとして出力される。) ログレベルはlogging.level.<ロガー名>という形式のプロパティで指定できる。 例えばコマンドラインから指定するなら以下の感じ。 java -jar build/libs/goslings-0.0.1.jar --logging.level.org.springframework.web=DEBUG 全ロガーのログレベルはlogging.level.rootで指定できる。 ロギング実装ライブラリの設定 ロギング実装ライブラリの設定ファイルをカスタマイズして、より詳細な設定をすることもできる。 Logbackの場合、クラスパスのルートに置かれたlogback-spring.xmlかlogback.xmlがロードされる。 設定ファイルの二重初期化を防いだりSpring Boot拡張設定を利用可能にするために、前者のファイル名が推奨されている。 (Groovyが使える環境ならlogback-spring.groovyでもいい。) いつものようにjavaコマンドでアプリを起動する場合は-jarオプションを使うため、-cpオプションでクラスパスを指定しても無視されてしまうので、基本はlogback-spring.xmlはjarの中に入れることになる。 プロジェクトのリソースディレクトリのトップ(デフォルトではsrc/main/resources/)にlogback-spring.xmlを置いておけば、GradleのJavaプラグインのprocessResourcesタスクによってjar内の適切な場所に取り込まれる。 logging.configプロパティで設定ファイルのパスを指定することもできる。
eyecatch
Fri, Jan 13, 2017

Goslings開発メモ - その3: Spring Boot続続編 (例外処理)

「Goslings開発メモ - その2: Spring Boot続編 (DI)」の続き。 Spring Boot続続編で、例外処理について。 (adsbygoogle = window.adsbygoogle || []).push({}); Spring MVCアプリにおける例外処理 Goslingsは前々回書いたようにspring-boot-starter-webというスターターを使っていて、つまりSpring MVCアプリだ。 Spring MVCアプリにおける例外処理についてはちょっと古いがこの記事に詳しい。 まず、Goslingsの構成で例外処理を何も書かなかった場合、コントローラのリクエストハンドラから例外が投げられると、ログにスタックトレースが出力され、クライアントにはHTTPステータスコード500 (Internal Server Error)とともに以下の様なデフォルトのエラーページが返る。 なんだかこれでも十分な気がするが、実際にはちゃんと明示的に例外処理をしたほうがいいだろう。 エラー時に返すHTTPステータスコードをカスタマイズしたり、遷移するページを変えたりしたくなるだろうから。 記事によれば、リクエストハンドラ内で例外をキャッチして処理するのはイケてなくて、関心事の分離のために別の場所に処理を書くのが良いらしい。 Spring MVCアプリにおける例外処理には以下の3つの段階がある。 投げる例外をカスタマイズする 例外クラス毎の例外ハンドラをコントローラに実装する コントローラ間で共用する例外ハンドラクラスを作る 以下それぞれについて書く。 1. 投げる例外をカスタマイズする リクエストハンドラから投げる例外に@ResponseStatusをつけることで、クライアントに返すHTTPステータスコード(とリーズンフレーズ)をカスタマイズできる。 例えば以下のような例外を投げると、HTTPステータスコード500 (Internal Server Error)の代わりに400 (Bad Request)がクライアントに返る。 @ResponseStatus(HttpStatus.BAD_REQUEST) public final class BadRequestException extends RuntimeException { // 省略 } 2. 例外クラス毎の例外ハンドラをコントローラに実装する コントローラのメソッドに@ExceptionHandlerをつけてやると、そのメソッドは例外ハンドラになり、そのコントローラのリクエストハンドラから特定の例外が投げられたときの処理を書くことができる。 さらに例外ハンドラに@ResponseStatusをつければ、HTTPステータスコードをカスタマイズできる。 例外ハンドラの戻り値はリクエストハンドラのと同様に処理されるので、遷移するページ等も自由にカスタマイズできる。 Goslingsでは、上記BadRequestExceptionからは@ResponseStatusを削除したうえで、RestApiV1Controllerに以下の様に例外ハンドラを書いた。 public final class RestApiV1Controller { // 例外ハンドラ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(BadRequestException.class) ErrorInfo handleBadRequestException(HttpServletRequest req, Exception ex) { return new ErrorInfo(req.getRequestURL().toString(), ex); } } (RestApiV1Controller.javaの完全なソースはこちら) こう書くと、RestApiV1Controllerの任意のリクエストハンドラからBadRequestExceptionが投げられると、handleBadRequestExceptionが呼び出され、HTTPステータスコード400 (Bad Request)とともにクライアントにHTTPレスポンスが返る。 RestApiV1ControllerはREST APIコントローラなので、このHTTPレスポンスのボディは、handleBadRequestExceptionの戻り値であるErrorInfoオブジェクトをJSONに変換したものになる。 例外ハンドラの仮引数は、上のコードに書いたもののほか、サーブレット関係のクラスなど(e.g.