Play! frameworkでつくるスクリーンショット取得サービス

このエントリーをはてなブックマークに追加
本エントリは Play! framework Advent Calendar 2011 jp #play_ja : ATND の5日目です。


シャノンCRM部の nishino です。
サポートの人となって、はや5ヶ月。主にトラブル対応がメインですが、お客様の声を直接耳にするようになり、温度感とか、つまずきポイントとか、技術部時代には「意味分からん」とか言っていたことがなんとなく分かるようになってきた気がします。

自分としては懇切丁寧に(手取り足取り)教えるとゆーよりは、お客様が自己解決できるようにドキュメントやツール群を整備していきたいなあ・・と考えている今日このごろです。

今回はシャノンのヱヴァンジェリスト @ikeike443 氏より
「Play!のAdvent Calendarやるよー。」
「ほう、いいねぇ」
と二つ返事で参加することになりましたが、このPlay!という代物は実行時デバッグでエラーが分かりやすく、あまりつまずくことなく扱えるのと(すばらしい)、公式英語サイト(http://www.playframework.org/)の日本語翻訳サイト(非公式)(http://playdocja.appspot.com/)がかなり内容充実しており(これまたすばらしい)、オレがあまり言うこともないなぁという印象で今は若干後悔しておる感じです。
Advent Calender days 4thの @Masahito さんもPlayの情報源としていろいろ紹介してくれていますので気になる方は是非見にいかれることをオススメします。

Play! framework Advent Calendar 2011 jp

とはまあ言ってもちょっとドキュメント整備の一環でスクリーンショット取得の仕組みが欲しかったので、さくっとPlay!でつくってみて、他のAdvent Calender参加者様(もちろんこのブログを見てくれているあなた様も)と、きゃっきゃ・うふふと楽しめたら御の字だよね、と思っております。

スクリーンショット取得サービス

URLを入力してスクリーンショットの画像を取得するサービスです。
既存のサービスに「ブラウザキャプチャサービス」や「HeartRails Capture」といったものがありますが、これをPlay! で作ってみたいと思います。

プロジェクト作成

プロジェクト名はhogeです。
好きな名前をつけるとよいと思います。

$ play new hoge
~ _ _
~ _ __ | | __ _ _ _| |
~ | '_ \| |/ _' | || |_|
~ | __/|_|\____|\__ (_)
~ |_| |__/
~
~ play! 1.2.3, http://www.playframework.org
~
~ The new application will be created in /Users/magnet/Workspace/play/hoge
~ What is the application name? [hoge]
~
~ OK, the application is created.
~ Start it with : play run hoge
~ Have fun!
~
$


CRUDモジュール

Play!に標準で用意されているCRUDモジュールは便利です。なぜなら色々やることがなまけられる省略できるからです。
このCRUD モジュールを有効にします。
hoge/conf/application.conf ファイルに次の行を追加します。

# Import the crud module
module.crud=${play.path}/modules/crud

ついでにデータベースを使用するために、 次の行を非コメント化しましょう。
db=mem

モデル

モデルのフィールドはスクリーンショットを撮りたいURLのみです。撮った画像はファイルで保存するのでモデルにはいれません(つくるとしたらBlob型とか)。
hoge/app/models/Screenshot.javaファイルを作成します。

package models;

import javax.persistence.Entity;
import play.db.jpa.Model;

@Entity
public class Screenshot extends Model {
public String url;

public Screenshot(String url) {
this.url= url;
}
}

コントローラ

CRUDモジュールをつかっているのでコントローラは超スッキリです。
hoge/app/controllers/Screenshots.javaファイルを作成します。

package controllers;

import play.*;
import play.mvc.*;

public class Screenshots extends CRUD {}

コントローラのクラス名は、モデル名(Screenshot)の複数形である Screenshotsにしとかないと動かないのでご注意。(設定より規約ってヤツです)

とりあえずここまでを動かしてみる

上記の作業を行った後で、Play!を実行してURLの一覧が表示されるのを確認してみます。

$play run

基本的にplayコマンドの実行はhogeディレクトリ(プロジェクトのトップディレクトリ)で行います。別ディレクトリで実行する場合は、[app_path]オプションを渡してあげればよいかと思いますが、めんどくさいのでね、、
(play(オプションなし)で表示されるUsage参照)

次のURLにアクセスします。
http://localhost:9000/screenshots/list




CRUDモジュールによりビューが自動生成されるので楽ちんですね。

スクリーンショットを取得するライブラリの追加

今回スクリーンショットを取得するライブラリは、ウチのQAでも大変お世話になっているseleniumのWebDriverを使用します。

WebDriverのライブラリ(jarファイル)を取り込むために依存ファイルを書きます。
今回はFirefoxでスクリーンショットを撮るのでFirefoxDriverを入れます。状況によってはInternetExplorerDriverでもいいと思います。
あと、ファイルの処理用にFileUtilsも欲しいのでApache Commonsも入れておきます。
hoge/conf/dependencies.ymlファイルに次の行を追加します。

- org.seleniumhq.selenium -> selenium-api 2.11.0
- org.seleniumhq.selenium -> selenium-firefox-driver 2.11.0
- org.apache.commons -> commons-io 1.3.2

次のコマンドを実行するとライブラリが取り込まれます。

$ play dependencies

ライブラリの検索は、JARVANA(http://www.jarvana.com/jarvana/)がオススメです。javadocも検索できるので重宝しております。

スクリーンショットを取得する処理の追加

それでは、登録したURLのスクリーンショットを取得する処理を追加します。
保存(save)ボタンが押されたら、スクリーンショットを撮ります。
hoge/app/controllers/Screenshots.javaファイルを編集します。
(さっきまでCRUDモジュールでスッキリしてたヤツです)

package controllers;

import java.io.*;
import java.util.List;
import play.*;
import play.mvc.*;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.apache.commons.io.FileUtils;

import models.Screenshot;

public class Screenshots extends CRUD {

@After( only={"create", "save"} )
static void capture( Long id ) {
String[] datas1 = params.getAll("object.url");
String urlpath = "";
for(String data1: datas1) {
urlpath = data1;
}

Screenshot screenshot = new Screenshot("");
if ( id == null ) {
List<Screenshot> datas2 = Screenshot.find(
"select s1 from Screenshot s1 " +
"where s1.id = (select max(s2.id) from Screenshot s2)").fetch();
for( Screenshot data2 : datas2 ) {
screenshot = data2;
}
} else {
screenshot = Screenshot.findById(id);
}

try {
WebDriver driver = new FirefoxDriver();
driver.get(urlpath);
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(scrFile,
new File("public/images/" + screenshot.id.toString() + ".png"));
driver.quit();
} catch (Exception e) {
Logger.info(e.getMessage());
}

}
}

少しだけ解説しますと、
まずcaptureメソッドを追加しているのですが、「@After( only={"create", "save"} )」で、create(新規作成)とsave(更新)の場合だけ、リクエストをインターセプトしています。

次に、入力したurlをパラメータマップであるparamsオブジェクトから取得します。

urlとモデルのidを結びつけたいので、idをcaptureメソッドの引数で取得しているのですが、新規作成時はidが渡ってこない(id == nullで返る)ので、findメソッドでidが最大のオブジェクトを検索しています。

WebDriverのgetScreenshotAsメソッドで取得したスクリーンショットの画像ファイルは、hoge/public/images ディレクトリに{id}.pngという名前で保存しています。

取得したスクリーンショットを表示する(ビュー)

ビューはどこでもいいのですが、一覧で見るのが壮観な気がしたので、今までCRUDモジュールで自動生成していた一覧テンプレートをカスタマイズします。

$ play crud:ov --template Screenshots/list

上のコマンドを実行すると、hoge/app/views/Screenshots/list.html にテンプレートが作成されます。
スクリーンショットの画像が表示されるようにimgタグを追加します。

#{crud.table fields:['id']}
#{crud.custom 'id'}
<a href="@{Screenshots.show(object.id)}">${object.id}:${object.url}</a><br />
<img width="250" src="/public/images/${object.id}.png">
#{/crud.custom}
#{/crud.table}

#{crud.table}編集後のlist.html

#{extends 'CRUD/layout.html' /}
#{set title:messages.get('crud.list.title', type.name) /}

<div id="crudList" class="${type.name}">

<h2 id="crudListTitle">&{'crud.list.title', type.name}</h2>

<div id="crudListSearch">
#{crud.search /}
</div>

<div id="crudListTable">
#{crud.table fields:['id']}
#{crud.custom 'id'}
<a href="@{Screenshots.show(object.id)}">${object.id}:${object.url}</a><br />
<img width="250" src="/public/images/${object.id}.png">
#{/crud.custom}
#{/crud.table}
</div>

<div id="crudListPagination">
#{crud.pagination /}
</div>

<p id="crudListAdd">
<a href="@{blank()}">&{'crud.add', type.modelName}</a>
</p>

</div>

こんな感じで撮ったスクリーンショットが表示されるようになりました。

まとめ

今回はスクリーンショットを取得するWebサービスをPlay! frameworkで作ってみました。
Play! はアイデアをすぐに動く形に出来るという点ですばらしいツールです。
まあ実用レベルのサービスを作ろうと思ったらフレームワークの機能だけでは足りなくて、自分でゴリゴリ書かなきゃダメなんでしょうが、社内ツールレベルだったら十分アスティルの写本だと思います。

Play! framework Advent Calender の明日 (12/6) は @syuta さんです。 明日もお楽しみに!

次の記事
« Prev Post
前の記事
Next Post »

2 コメント

Write コメント
nishino
AUTHOR
2011年12月5日 11:44 delete

@ikeike443 からのツッコミ(感謝!)をいくつか修正しました。
http://twitter.com/ikeike443/status/143366277698826240
http://twitter.com/ikeike443/status/143367137241731074
http://twitter.com/ikeike443/status/143369657343156225

FileUtilsの件は、確認したら修正します。

Reply
avatar
2012年2月18日 17:11 delete

とても魅力的な記事でした!!
また遊びに来ます!!
ありがとうございます。。

Reply
avatar
Related Posts Plugin for WordPress, Blogger...