vol.3

Magazine

Japan Grails/Groovy User GroupJGGUG Camp2011

by torazukaby 合宿運営レンジャー

日本中から G* 好きなエンジニアが集い、温泉宿に泊まり込みで、プログラミングに、LT 大会に、そして懇親会に楽しい時を過ごします。開催概要は以下の通り：

日程：11/4( 金 ) 〜 11/6( 日 ) ※一泊目はオプション
会場：箱根 美肌の宿 きのくにや http://hakone-kinokuniya.co.jp/
費用（概算）：一泊なら￥ 8,500+ α、二泊なら￥17,000+ α。
αは懇親会費（￥2,000 前後？）
内容（予定）：• LT 大会• 懇親会• スペシャル企画• 各自の自由テーマ など

JGGUG では、一昨年、そして昨年秋と熱海で合宿を開催してきました。昨年は g100pon をいうスペシャル企画で、100 個のプログラムの「お題」にリモート参加組を含めた 16 名で取り組み、合宿開催中だけで 75% 以上をクリア、たくさんの有用なサンプルコードを生み出しました。過去の合宿の様子は以下をご覧ください：・JGGUG 合宿 2009 レポート
http://www.jggug.org/eventarchive/jggugcamp2009/report・togetter: JGGUG 合宿 2010
http://togetter.com/li/58838
今年は、原稿執筆時点ですでに 15 名の方（うち初参加の方 5 名を含む）が参加予定になっています。参加者は引き続き募集中ですので、興味のある方は下記の Google グループに参加の上、合宿参加の意思表示をお願いします：
・JGGUG 合宿プロジェクト 2011https://groups.google.com/group/jggugcamp2011
場所や日程があわず、残念ながら参加できないという方もご心配なく。今年も Twitter や Ustream などをフル活用し、リモートからも楽しんでいただける体制を整える予定です。（リモートを含め）今年も多
くのみなさまの参加をお待ちしています！

Contents
Series 01

Grails をコントロールせよ ! Part2………… 4
Series 02

Griffon 不定期便

〜第４回 スレッド編その２〜… ……………………… 10
Series 03

CodeNarc を利用して GROOVY のコード品質を上げる〜第 3 回 ルールのカスタマイズ〜… ………………… 16
Series 04

Geb で始める Web テスト〜第 2 回 Geb0.6 編〜…………………………………… 19
Series 06

もし新人女子 Java プログラマが『Groovy イン・アクション』を読んだら〜第２章 もかは『プログラミング Groovy』と出会った〜… …… 24
特別寄稿

GBench in a Nutshell… ………………………… 29
Series 05

Grails Plugin 探訪〜第 4 回 Cloud Foundry プラグイン〜… …………… 33
JGGUG ４コマ漫画「ぐるーびーたん」第 3 話… …… 45
Information

リリース情報…………………………………………… 46

G*Magazine vol.3

Grailsをコントロールせよ! Part 2
山本
series01

剛（株式会社ニューキャスト）

出版・印刷関連のシステム設計開発等に従事するテクニカル DTP アーキテクト。
日本 Grails/Groovy ユーザーグループ名古屋支部長。2006 年より Grails のドキュメント翻訳、
その後、Grails 公式の Acegi プラグインを開発。書籍『Grails 徹底入門』( 翔泳社発行 ) の 9、10、11 章を執筆。

前号にて、筆者が別の記事で紹介したように、最新のマイルス同じと言うことは、今回は何を修得すれば良いのでしょう？

されました。コントローラの部分にも若干の仕様変更・追加が行して決まったルールでリクエストパラメータを送信することでバら、なるべく部分的にでも将来の Grails での追加仕様についてもこの部分です。今回は主に、リクエストパラメータによって変トーンリリースは、Grails 1.4.0 系、改め Grails 2.0.0 系がリリース

われました。この記事では、引き続きコントローラを解説しながリレーショナルデータバインドでは、リレーショナル形式に対インディングを行います。

紹介していきたいと思います。

わる、データバインディングの振る舞いを解説します。先ずは基プにて発表させていただいたスライド http://slidesha.re/q3tuH6、

many-to-one）から見ていきましょう。

Grails 2.0.0 系に関しての詳しいまとめは、筆者のワークショッ

または、リリース情報をまとめたブログ記事 http://bit.ly/rms1Inをご覧ください。（執筆時は Grails 2.0.0.M1 です）■ 多対１、１体１の連携

注）この記事は、公式ドキュメントを意訳している筆者が、公式ドキュメントの内容を筆者目線で再編集（感想文 !?）したような感じの構成になっています。一部、公式ドキュメント意訳と同じ内容を転載しているような部分もありますが、筆者が訳者なので致し方ないのです。ご理解とご了承お願いします

礎ルールとなる部分の解説のために、
単一終端関連（one-to-one、。

次のクラスを例とします。
class Book {Author author}
class Author {....

ドメインとコントローラの更なる連携を！
前回はバインディングで単純なパラメータのバインドを行いました。おさらいとして、次のようなコードです。今回もこの記述が基本となります。

def user = new User(params)// またはdef user = new User()user.properties = params

リレーショナルデータバインド
Grails のデータバインディングでは、リレーショナルが存在す

るドメインクラスの場合でも、データバインディングを簡潔に実行できます。

データバインディングのコントローラでの記述は、基本的には先程のおさらいコードと全く同じになります。これもおさらいになりますが、データバインディングには、主に３つの方法があります。

1. ドメインクラスのコンストラクタに params を渡す。

2. ド メ イ ン ク ラ ス イ ン ス タ ン ス の properties プ ロ パ テ ィ にparams を代入する。

3. コ ントローラの bindData メソッドでドメインクラスインス
タンスと params をバインドする。

4

Grails をコントロールせよ！ Part 2}
one-to-one または many-to-one 等の単一終端関連のあるドメ

インでは、対象のリクエストパラメータに ".id" をサフィックスとして付加して指定します。次のように指定することで、データ

バインディング時に、値として与えられた id の Author クラスを検索します。また、リクエストパラメータに文字列で "null" と指定すると、対象の関連には null がセットされます。
// 多対１、１対１の連携の指定。
/book/save?author.id=20// 値に文字列で null をあたえる。
/book/save?author.id=null// データバインディング
def b = new Book(params)
ここで覚えておいて欲しいのが、Grails でのリクエストパラ

メータのルールでは、Groovy でのクラスのプロパティ参照と同じように、リクエストパラメータのパラメータ名に ".
（ドット）"
を使用してパラメータに値をあたえると、その対象を更新する事になります。

つまり、この例ではリクエストパラメータ author.id を、ドメ

インクラス Book のプロパティauthor の該当クラス Author にあるプロパティ id として認識して、指定された値をセットしたという事になります。

G*Magazine vol.3
このルールをもっと活用すると、さらに細かな事が可能になります。次のマルチドメインバインドで説明します。

ラアクションでのコード記述は次のようになります :def b = new Book(params.book)■ マルチドメインバインド

def a = new Author(params.author)

先程のリクエストパラメータでの ”.（ドット）”ルールでは ”id”

だけを指定しましたが、同じ方法でパラメータを追加して、関連

コ ラ ム

したドメインクラスの別のプロパティを更新することも可能です。

リクエストパラメータのルールは、便利なのと同時に危険性も持ち合わせています。

// リクエストパラメータ

この機能があることで、Web サイトが Grails で開発されて/book/save?author.id=1&title=
TheBook&author.name=tyama// コントローラアクションでの処理いて、且つフォームの内容を探りを入れて、パラメータが分かれば、関係の無いフォームからの関連ドメインの更新ができてしまいます。

ここでは説明のため GET メソッドの形で紹介をしています

def bookInstance = new Book(params)
この例では、新規 Book を作成する際に、author.id=1 で Author

を 取 得 し て Book に 設 定 し つ つ、author.name=tyama で 同 時 にAuthor のプロパティ name が更新されます。

更にこのルールを活用して、複数ドメインクラスをデータバイ

ンディングすることが可能です。
次のリクエストを例とします :が、先ず保存更新などを GET で実装することは無いと思うので（デフォルトで CRUD の save 等は POST です。
）、URL から簡
単にイタズラされることは無いでしょう。但し POST 等で攻撃されることもあると思うので設計全体での対策も必要です。

簡単な対策として、関連ドメインの更新の必要無いときは、

単に params から必要なプロパティだけを選んで更新する方法を使用する :

book.properties['title','isbn'] = params/book/save?book.title=TheStand&book.
pages=400&author.name=StephenKing
こ の 場 合 は、 そ れ ぞ れ リ ク エ ス ト パ ラ メ ー タ が "book."、または、更新に関係の無い関連ドメインは、ドメインクラ

スの discard() メソッドで更新を制御する方法も考えられます。
// 保存の前に"author." で始まっています。もうわかると思いますが、接頭辞とbookInstance.author.discard()

して付加した内容によって、データバインドを行うドメインクラスを区別できるようにしています。この内容で受け取ったパラ

用途に応じて対策しましょう！

メータは、多次元ハッシュマップである params オブジェクトに、それぞれ区別されて登録されます。

リクエストパラメータの内容は、Grails によって処理されて、params オブジェクト（ミュータブル多次元ハッシュマップ）へ登録されます。その中を見てみましょう。
// リクエストパラメータ/book/save?book.title=Groovy&book.

■ 複数終端関連 - １体多、多対多の連携

Author に複数の Book を持つ関連、次のドメインクラスを例とします。

class Author {static hasMany = [books: Book]

pages=303&author.name=ksky//params の内容

String name}[book.title:Groovy,class Book {book:[title:Groovy, pages:303], // ここに注目

String title
Publisher publisher

author.name:ksky,

author:[name:ksky], // ここに注目}

book.pages:303,

class Publisher {String nameaction:save, controller:book]}

book:[title:Groovy, pages:303] と author:[name:ksky] の よ う に、Set 型の関連ドメイン（hasMany でのデフォルト）の更新には、自動的にサブセットとして保持されているので、それらを対象のドメインクラスに渡せば良いと言うことになります。コントロー

更新を行う対象の id を配列で送信します。次のタグでセレクトボックスを作成します。

Grails をコントロールせよ！ Part 25

G*Magazine vol.3<g:select name="books"// リクエスト例

from="${Book.list()}"/save?name=tyama&books[0].title=Grails%20

size="5" multiple="multiple"

Ref&books[0].publisher.id=4

optionKey="id"//params の内容

value="${author?.books}" />[books[0].publisher.id:4,

この GSP タグで次のような複数セレクトボックスが生成され// ここが Book に関連します。

ます。

books[0]:[publisher.id:4,
publisher:[id:4],
title:Grails Ref],
name:tyama,books[0].title:Grails Ref]// コントローラアクション
リクエストパラメータから、params に収集された内容は次のようになります。books:[2,3] の部分がセレクトボックスからの内容になります。

def author = new Author(params)
author.save()
既存のデータに対して、更新を行うことも可能です。次の例では、関連ドメイン Book のタイトルを変更します。

// ①複数選択した場合[books:[2, 3], name: 関谷和愛 , create:Create,action:save, controller:author]// ②１つのみ選択した場合[books:1, name: 山本剛 , create:Create,// リクエスト例/?id=3&name=tyama&books[0].title=Grails Guide//params の内容[

name:tyama,

ここで気付くのが、選択された内容の件数の違いです。①の内books[0].title:Grails Guide,

容は配列にですが、②の内容は配列ではありません。Grails では、

books[0]:[自動でデータバインディングする場合は、対象のプロパティがtitle:Grails Guide // ←変更

Set 型などの場合は、自動的に配列として判別してくれます。

List 型、Map 型ベース関連の場合は、Set と違い順序とインデッ

クスを指定することができるので、添字演算子を使用して関連ドメインのプロパティを更新・追加する事が可能です。

次のリクエスト例のように、リクエストパラメータに添字演算子を使用してフォームが送信されたとします。この例では、
publisher と title を指定した Book 用の値で新規に Book を作成しつつ新規に Author を作成しています。

// ← id を指定

id:3,

action:save, controller:author]]]// コントローラアクション
def author = Author.get(params.id)
author.properties = params
author.save()
関連ドメインの新規追加も可能です。この例では先の例のデー

タに対して、title の内容を変更と新たに Book を追加します。対
象のデータが持っている関連ドメイン（例では books）の元の数よりも多いデータをバインドした場合は、自動的に新規追加されます。※但し関連ドメインの制約エラー等で追加・更新できない
場合は無視されます（現状エラーメッセージも表示しません）。

6

Grails をコントロールせよ！ Part 2

G*Magazine vol.3// リクエスト例/?id=3&name=tyama&books[0].title=
Grails Guide 2&books[1].title=Grails 2.0//params の内容 （一部省略）

ドしたインスタンスを生成します。
また、複数のコマンドオブジェ
クトを指定することも可能です。
class LoginController {def login = { LoginCommand cmd ->[if (cmd.hasErrors()) {id:3,

redirect(action: 'loginForm')// 既存の title を更新

returnbooks[0]:[title:Grails Guide 2],}// 追加するデータ// ここに処理。

books[1]:[title:Grails 2.0],
name:tyama

}

]

}

def author = Author.get(params.id)

コマンドオブジェクトには依存注入 (DI) も可能です。ドメイン// コントローラアクション
author.properties = params

クラスやコントローラと同じように指定します。

author.save()
あとはルールの応用になります。詳しくは公式ドキュメントを

class LoginCommand {def loginService //loginService ビーンを依存注入

参照してください。

String username

コマンドオブジェクト

String password

コマンドオブジェクトは Struts での Form ビーンと同じような

static constraints = {オブジェクトです。ドメインクラスのような永続化が必要無く、

username validator: { val, obj ->データバインディングとバリデーションが必要な場合に使用しまobj.loginService.

す。例えば、簡単なユーザ認証のフォームを想像してみてくださ

canLogin(obj.username,い。ユーザ認証では、アカウント、パスワード等のフィールドを

obj.password)

必要として、それぞれフォームでのバリデーションとして、空文}字列禁止（必須項目）や、必要で有れば文字数制限が必要な場合があります。

コマンドオブジェクトは、通常使用するコントローラと同じファイルに、クラス名が Command で終わる名称で記述します。
この例ではドメインクラスと同じように制約（constraints）も定義しています。（※ドメインクラスの詳細に関しては、今後執筆予定です。詳細は公式ドキュメントを参照してください。
）
class UserController {}}便利な共通処理と制御
コントローラには、データバインディングだけで無く、他にも、前後処理を行うインターセプタ、フォーム２重送信制御などコントローラで活用できる様々な制御の仕組みが存在します。これらの機能を活用して、セキュリティを高めたり、共通処理をスマー
トに記述する事が可能です。

...
}
class LoginCommand {String username
String password
static constraints = {username(blank: false, minSize: 6)
password(blank: false, minSize: 6)}}
コントローラアクションの引数にコマンドオブジェクトを指定すると、コマンドオブジェクトにリクエストパラメータをバイン■ デフォルトアクション

コントローラのルート URI にデフォルト URI を指定する方法です。スカッフォルド生成や create-controller コマンドで生成されたコントローラは、コントローラのルート URI が、/index になります。例として BookController の場合、ルート URI は /book でアクセスすると /book/index の内容が返ります。この動作にはルールがあります。

• アクションが１つの場合は、そのアクションがデフォルトになります。

• index という名称のアクションがある場合は、それがデフォルトになります。

Grails をコントロールせよ！ Part 27

G*Magazine vol.3• defaultAction を定義して指定できます。
static defaultAction = "list"

この例と同じように、マップを指定する条件指定が他にもあります。

複数のアクションを指定 :def beforeInterceptor = [action: this.&auth,■ インターセプター

同一コントローラ内の、全て、あるいは指定した複数のアクションに、前処理と後処理を行うインターセプターを追加することができます。ユーザ認証やロギングなどに活用できます。
イ ン タ ー セ プ タ ー は、 コ ン ト ロ ー ラ に、beforeInterceptor とafterInterceptor のプロパティで記述します。

except: ['login', 'register']]
指定したアクションのみで実行、only: を使用します :def beforeInterceptor = [action: this.&auth,only: ['secure']]● beforeInterceptor● afterInterceptor

加できます。beforeInterceptor に追加した処理で返値を false にます。afterInterceptor では処理結果のモデルを引数として取得

幾つかの記述方法があります。

できます。この例では返されたモデルの内容を元にビューを変更

beforeInterceptor では、アクションが実行される前に処理を追すると、アクションは処理を行いません。beforeInterceptor には

コ ン ト ロ ー ラ 内 全 て の ア ク シ ョ ン に 反 映 さ せ る に は、

afterInterceptor はアクションが実行された後に処理を追加しして、モデルやレスポンス（ModelAndView）を操作することがしています

beforeInterceptor プロパティに、コードブロックを指定して処理を記述します。

def afterInterceptor = { model, modelAndView ->println "Current view is${modelAndView.viewName}"def beforeInterceptor = {if (model.someVar) modelAndView.viewName =println "Tracing action ${actionUri}""/mycontroller/someotherview"}

println "View is now

指定したアクション以外に処理を適用する場合は、実行するメ

ソッドと、except 条件を指定します。

コントローラ内の login アクション以外の全ては、ユーザがロ${modelAndView.viewName}"}

グインしていないと login アクションへ遷移する処理の例です。
beforeInterceptor プロパティにマップの形式で、実行するメソッ

ド名を action: に指定、除外するアクション名を except: に指定しています。

■ メソッド制御

コントローラ内のアクションに対して HTTP メソッドの制御を行うことができます。allowedMethods プロパティにマップ形式

def beforeInterceptor = [action: this.&auth,except: 'login']private auth() {で、アクション名に対して許可をする HTTP メソッドを指定します。

static allowedMethods = [save: "POST",
update: "POST", delete: "POST"]

if (!session.user) {redirect(action: 'login')
return false}}■ フォーム２重送信制御

送信ボタンの２度押しなどで２重登録になってしまう等、何も制御しないと発生しますよね？実装方法は色々あると思います

def login = {// ログインページ}

が、Grails では、" 同期トークンパターン " を使用した、フォーム
二重送信ハンドリングに対応しています。

▼使用方法は GSP タグのフォームに useToken 属性を追加します。

<g:form useToken="true" ...>
コントローラアクションでフォーム２重送信制御用のwithForm メソッドを使用して、リクエストの制御処理を行います。

8

Grails をコントロールせよ！ Part 2

G*Magazine vol.3

withForm {// 有効なリクエスト}.invalidToken {// 無効なリクエスト}
invalidToken は省略することもできます。省略した場合は、無効のトークンを flash.invalidToken に保持して元のページへリダ

イレクトします。元のページで、次の例のようにビューでトーク
ンを使用します。

<g:if test="${flash.invalidToken}">
ボタンが２度クリックされました。

</g:if>（この例はあまり使わないと思いますが。。。）

まとめ
今回はここまでとします。ここまでのコントローラの解説では、コントローラでのリクエストの制御までを解説してきました。
データバインディングや各種コントローラでの制御機能は、便利に活用できる反面、実装での使い方によっては、ルールに基づいた部分の危険性も出てきます。それぞれの機能を上手く使用して、便利な機能で効率よくセキュアなコントロールを心がけましょう。

さて次回は、コントローラ第３弾！「Grails をコントロールせよ！ Part 3 - URL をコントロールせよ！」
です。
Grails での URL マッ
ピング、フィルタ、REST を解説します。それではまた次号で！

Grails をコントロールせよ！ Part 29G*Magazine vol.3

Griﬀon 不定期便

〜第 4 回 スレッド編 その 2〜series02

奥 清隆（おく きよたか）
仕事でもときどき Groovy と戯れるプログラマ。
日本 Grails/Groovy ユーザーグループ関西支部長。
著書：『Seasar2 による Web アプリケーションスーパーサンプル』

今 回 は 簡 単 な Twitter 検 索 ア プ リ を Griffon で 作 成 し な が ら、
Griffon でのスレッドについて紹介していきます。

ラクティブモードで実行することをお勧めします。インタラク
ティブモードの実行は次のコマンドで起動できます。
griffon interactive
作成したアプリケーションのディレクトリに移動してからイン

タラクティブモードで起動すると次のようにコマンドの入力を待つようになります。

$ griffon interactive
Welcome to Griffon 0.9.3 - http://griffon.codehaus.org/
Licensed under Apache Standard License 2.0
Griffon home is set to: /usr/local/griffon/current
Base Directory: /workspaces/griffon/twitterSearch------------------------------------------------Interactive mode ready. Enter a Griffon commandor type "exit" to quit interactive mode (hit
ENTER to run the last command):
試しにこの状態で「 run-app 」と入力して Enter キーを押してみましょう。最初のサンプルウインドウが立ち上がりましたが、普通に「 griffon run-app 」とするよりかは早く起動したと思いませんか？（思わなかった？）それでは今立ち上がっているウイ
サンプルアプリケーションのソースコードは以下のサイトから

ダウンロードできます。

https://github.com/kiy0taka/twitterSearch

ンドウを閉じてみましょう。するとまたコマンド入力を待機する状態になります。もう一度「 run-app 」を入力してみましょう。
今度はさらに早く感じられたと思います。インタラクティブモー

ドで「 run-app 」を 2 回実行しましたが、前回と同じコマンドを実行する場合は Enter キーの入力だけで前回のコマンドを実行でアプリケーションの作成
今回は最新版のバージョン 0.9.3 を使用します。インストール

がまだの方は G*Magzine 準備号の「Griffon 不定期便 01」を参考にインストールして下さい。バージョンは異なりますが、同じ方法でインストールできます。

まずは、アプリケーションを作成しましょう。作成するアプリ

ケーションは「TwitterSearch」という名前にします。
griffon create-app twitterSearch

今回は少しづつ作成しながら解説を進めていこうと思います。

そのため何回もアプリケーションを起動しますが、毎回 griffon

コマンドを実行していると起動に時間がかかりますので、インタ

10 Griffon 不定期便 〜第 4 回 スレッド編 その 2 〜

きます。少し修正してからアプリケーションを起動して確認するにはインタラクティブモードで実行するのがお勧めです。

それではアプリケーションの View 部分から実装していきま

しょう。検索用テキストフィールドと検索ボタン、そして検索結果をテーブルに表示するようにします。griffon-app/views/
twittersearch/TwitterSearchView.groovy を次のように変更します。

G*Magazine vol.3

package twittersearchpanel(constraints:NORTH) {textField columns:15

button ' 検索 ', actionPerformed: {import static java.awt.BorderLayout.*

searchResult.rowsModel.value = [application(title: 'TwitterSearch',[fromUser:'kiy0taka',

text:'Griffon なう。'],

preferredSize: [350, 400],
pack: true,[fromUser:'kiy0taka',

text:'@groovybook G マガ読んだよ！ ']locationByPlatform:true,
iconImage: imageIcon('/griffon-icon-48x48.png').image,]iconImages: [imageIcon('/griffon-icon-48x48.png').image,searchResult.fireTableDataChanged()

imageIcon('/griffon-icon-32x32.png').image,
imageIcon('/griffon-icon-16x16.png').image]){}}scrollPane(constraints:CENTER) {table {

panel(constraints:NORTH) {tableModel(id:'searchResult') {propertyColumn header: 'User',textField columns:15
button ' 検索 'propertyName: 'fromUser'
propertyColumn header: 'Tweet',}propertyName: 'text'

scrollPane(constraints:CENTER) {}table()}}}
図のようなウインドウになりました。レイアウトはこれで問題なさそうです。

}
検索ボタンのイベント処理と、
テーブルモデルを追加しました。

これを実行して、検索ボタンをクリックするとテーブルにデータが表示されます。

少しだけアプリケーションに動きをつけてみたいと思います。

まずは検索ボタンをクリックしたときに検索結果がテーブルに表示されるようにしてみましょう。ここではまだ Twitter から検索
せずにダミーデータを表示するようにします。

なんとなくアプリケーションの動作がわかってきたので、検索ボタンのイベント処理をコントローラに移しましょう。griffonapp/controllers/twitersearch/TwitterSearchController.groovy を
次のように修正します。

Griffon 不定期便 〜第 4 回 スレッド編 その 2 〜11

G*Magazine vol.3

package twittersearchclass TwitterSearchController {import groovy.model.ValueHolder...
panel(constraints:NORTH) {textField columns:15,

def model

text: bind(target:model, 'searchText')

def view

button ' 検索 ', actionPerformed:
controller.&search,def search = {view.searchResult.rowsModel.value = [[fromUser:'kiy0taka',

text:'Griffon なう。'],[fromUser:'kiy0taka',

enabled: bind{model.searchText}}
def rowsModel = new ValueHolder()scrollPane(constraints:CENTER) {table {

text:'@groovybook G マガ読んだよ！ ']tableModel(id:'searchResult',]

rowsModel:rowsModel) {view.searchResult.fireTableDataChanged()

propertyColumn header: 'User',}propertyName: 'fromUser'}

propertyColumn header: 'Tweet',propertyName: 'text'

View にあったイベント処理を Controller の search クロージャ}に移動しました。Controller 内では View のコンポーネントにアクセスするために「 searchResult 」を「 view.searchResult 」に変更しています。

View の方ではイベント処理をコントローラに委譲するように検索ボタンを次のように変更します。

button ' 検索 ', actionPerformed:
controller.&search}}bean rowsModel, value:bind{model.tweets}
Controller の方も少し変えましょう。検索結果を直接 View に設定するのではなく、Model に設定するようにします。
def search = {model.tweets = [それでは次に Model を変更しましょう。テキストフィールドの値と検索結果に表示する一覧のデータを Model に定義します。

[fromUser:'kiy0taka',を次のように修正します。

[fromUser:'kiy0taka',

text:'Griffon なう。'],

griffon-app/models/twittersearch/TwitterSearchModel.groovy

text:'@groovybook G マガ読んだよ！ ']]package twittersearch
import groovy.beans.Bindable
class TwitterSearchModel {@Bindable String searchText@Bindable List tweets}
Model の値を View にバインドするように View を変更します。

バインディングについては G*Magazine 創刊号の「Griffon 不定期便」を参照ください。テキストフィールドの値を Model にバインドし、テキストフィールドが入力されている場合にのみ検索ボ

タンを有効化します。テーブルに表示するデータも Model から
バインドするようにします。

12 Griffon 不定期便 〜第 4 回 スレッド編 その 2 〜

view.searchResult.fireTableDataChanged()}
それでは、実際に Twitter から検索するようにしてみましょう。

Twitter4J のライブラリの設定が必要ですが、Griffon には Twitter
プラグインがあるので今回はプラグインをインストールして使ってみましょう。Twitter プラグインのインストールは次のコマンドでインストールできます。

install-plugin twitter
Twitter4J の API を 利 用 し て Twitter か ら 検 索 す る よ う にController を変更します。

G*Magazine vol.3
す。

package twittersearchgriffon -Dgriffon.disable.threading.
injection=true compile

import twitter4j.*

アプリケーション内のすべてのコントローラのアクションがclass TwitterSearchController {EDT 上で実行されます。

• griffon-app/conf/BuildConfig.groovy に指定する

def modelアプリケーション内のすべてのコントローラのアクションがdef view

EDT 上で実行されます。

def twitter• ${USER_HOME}/.griffon/settings.groovy に指定する

settings.groovy がなければ新しく作成し、「 griffon.disable.

def search = {threading.injection=true 」を追記します。この方法ではすmodel.tweets = twitter.

べてのアプリケーションのコントローラのアクションが EDT

search(new Query(model.

上で実行されるようになります。

searchText)).tweets
view.searchResult.fireTableDataChanged()}

これらの方法は 0.9.2 以前のバージョンと互換性を保つために}用意されている仕組みを利用します。0.9.2 以前のバージョンをController で twitter と い う 名 前 の 変 数 を 定 義 し て お く といいかもしれません。しかし、0.9.2 移行のバージョンではバッ

Twitter プラグインによって twitter4j.Twitter 型のオブジェクト

0.9.2 以上のバージョンにアップグレードする場合はこの方法がクグラウンドスレッド上で実行するのが推奨されるためこれらのが設定されます。Twitter プラグインは他にも色んな機能があり

方法はあまりおすすめしません。

方は調べてみてください。

■ アノテーションで実行するスレッドを指定する

ますが、今回は特に使用しないので解説はしません。興味のある

Griffon でのスレッドの扱い
さて、ここまででとりあえずのアプリケーションが実装できまコントローラのクロージャに @Threading アノテーションを指定することで、実行するスレッドを指定することができます。次のように指定するとアクションが EDT 上で実行されます。

した。ここからがやっと本題です。ここまでで出来たアプリケー

import griffon.transform.Threadingになっている箇所があります。

class MyController {ションは一見正しく実装しているように見えますが、まずい実装
Griffon ではバージョン 0.9.2 からコントローラに定義されてい@Threading(Threading.Policy.INSIDE_UITHREAD_SYNC)

るクロージャはすべてバックグラウンドスレッド上で実行される

def someAction = {ようになりました。作成したアプリケーションのコントローラで...

は特に意識せず、Model や View を変更しました。しかし、これはまずい実装です。View や Model はイベントディスパッチスレッ
ド上で変更する必要があります。Griffon 0.9.2 以前のバージョン

ではコントローラのアクションはすべてイベントディスパッチス
レッド上で呼び出されたのでこの実装でもあまり問題はありません。
（後述しますが、少し問題は残っています。）この問題を解決するためにはいくつか方法があります。

■コ ントローラのアクションをすべて EDT 上で実行されるようにする0.9.2 からコントローラのアクションがすべてバックグラウン

ドスレッド上で実行されますが、0.9.2 以前のバージョンと同じようにすべてのコントローラのアクションを EDT 上で実行できるようにします。以下のいずれかの方法で「 griffon.disable.
threading.injection=true 」を設定しておけば EDT 上で実行されるようになります。

• コンパイル時にシステムプロパティとして指定する

	以下のようにシステムプロパティを指定してコンパイルしま}}
アノテーションに Threading.Policy を指定することで実行するスレッドを指定できます。指定可能な Threading.Policy の値は次のとおりです。

• OUTSIDE_UITHREAD

バックグラウンドスレッド上で実行されます。Threading.
Policy を指定しない場合のデフォルト値となります。

• INSIDE_UITHREAD_SYNC

アクションが EDT 上で実行され、処理が終わるのを待ちます。SwingUtilities#invokeAndWait() に相当します。

• INSIDE_UITHREAD_ASYNC

アクションが EDT 上で実行されるようにスケジュールされます。SwingUtilities#invokeLater() に相当します。

• SKIP

スレッドの管理を行いません。

Griffon 不定期便 〜第 4 回 スレッド編 その 2 〜13

G*Magazine vol.3■ Griffon が提供するメソッドを利用する

前回紹介した、SwingBuilder の edt() 、doLater() 、doOutside()と同じようなメソッドが Griffon でも定義されています。実装はGriffon 独自になっていますが、SwingBuilder のメソッドとの対

def tweets =twitter.search(new Query(model.searchText)).tweets
execAsync {model.tweets = tweets

応は以下のとおりです。
GriffonSwingBuilder

execSync()

edt()

execAsync()

doLater()

execOutside()

doOutside()

執筆時点の最新版のソースコード（Groovy 1.8.1、Griffon 0.9.3）

を見ると、doOutside() は新しいスレッドを生成して実行するのview.searchResult.fireTableDataChanged()}
これで、めでたくスレッドセーフな実装になりました。

アプリケーションを更に改良
アプリケーションがスレッドセーフになり、問題はなくなりまに対し、execOutside() はスレッドプールを利用しているなどのした。しかし、ユーザインターフェースとして不親切な点がありソッドを利用して今回のアプリケーションを正しく実装してみま

での間、何も変化がないように感じられます。ユーザは本当に検違いはありますが、ほぼ同等の機能となっています。これらのメます。検索ボタンをクリックしてから、検索結果が表示されるましょう。

索をしているのか不安になり、何回も検索ボタンをクリックして何が問題？

ボタンをクリックできないように変更してみましょう。

正しく実装してみましょうといいましたが、今の実装は何が正しくないのでしょうか？一見問題なさそうにも見えますし、実行

してみてもまずそうな動きはしていないように見えます。
しかし、この実装には問題があります。Swing のスレッドポリシーを思いしまうかもしれません。検索中はプログレスバーを表示し、検索

まず、検索中であることを示すプロパティをモデルに追加します。

class TwitterSearchModel {@Bindable String searchText

出してみましょう。

通常、Swing はスレッドに対して安全ではありません。すべてのSwing コンポーネントと関連クラスには、特に説明がないかぎり、
イベントディスパッチスレッド上でアクセスしてください。

今のコントローラの search アクションはバックグランドス

レッド上で実行されるようになっています。モデルのプロパティを変更し、View に対しても処理をしていますが、これれはすべてバックグラウンドスレッド上で実行されてしまうため、スレッ@Bindable List tweets@Bindable boolean searching}
次に、コントローラで searching プロパティを設定するようにします。

def search = {execSync {ドセーフではありません。もっと複雑な処理をした場合、表示がmodel.searching = true

壊れてしまう可能性があります。

}

それでは、正しい実装に変えてみましょう。Twitter 検索をす

def tweets =理時間が予想以上にかかってしまう可能性があります。このようexecAsync {twitter.search(new Query(model.searchText)).tweets

る処理はネットワークの状態や、Twitter の負荷状況によって処な時間のかかる可能性のある処理は EDT 上で実行するのは好ま

model.tweets = tweetsでしょう。しかし、検索結果をモデルに設定してしまうとバイン

model.searching = falseview.searchResult.fireTableDataChanged()

しくないため、バックグラウンドスレッド上で実行する方が良い
ディング機能によって View が変更されてしまうため戻り値を直接モデルに設定するのは良くありません。一度ローカル変数に保持するように変更しましょう。

def tweets = twitter.search(new Query(model.
searchText)).tweets
model.tweets = tweets
view.searchResult.fireTableDataChanged()
さらに、モデルと View に対する処理を EDT 上で行うようにします。

14 Griffon 不定期便 〜第 4 回 スレッド編 その 2 〜}}
searching プロパティの変更も EDT 上で行います。また、プロ

パティの値を変更してから検索するためにここでは execSync()を利用しています。

検索が終わって View に反映したら searching プロパティを

false に戻しておきます。

最後に View にプログレスバーを追加し、searching プロパティ

で表示を制御しましょう。

G*Magazine vol.3

panel(constraints:NORTH) {textField columns:15,
text:bind(target:model, 'searchText'),
enabled:bind{!model.searching}button ' 検索 ',actionPerformed:controller.&search,
enabled:bind{model.searchText &&!model.searching}}
def rowsModel = new ValueHolder()scrollPane(constraints:CENTER) {table {
tableModel(id:'searchResult',
rowsModel:rowsModel) {propertyColumn header: 'User',propertyName: 'fromUser'
propertyColumn header: 'Tweet',propertyName: 'text'}}}
bean rowsModel, value:bind{model.tweets}
progressBar constraints:SOUTH,
visible:bind{model.searching},
indeterminate:bind{model.searching}
プログレスバーの表示切替だけではなく、検索条件の編集付加と検索ボタンの無効化も制御しました。それではアプリケーションを実行してみましょう。

検索中はプログレスバーが表示されれるようになり、ユーザに優しいインタフェースになりましたね。実はこのアプリケーショ
ンはまだ不完全です。エラー処理を実装していません。検索時にエラーが発生しても、ユーザには通知されずプログレスバーが動いたままになってしまいます。今回はエラー処理の実装と解説はしません。興味のある方はぜひ実装してみてください。

まとめ
前回と今回で Swing アプリケーションのスレッドポリシーと、

それに基づいた SwingBuilder と Griffon の使い方を紹介しました。普段 Web アプリケーションを実装されている方には少しわ
かりにくい解説だったかもしれませんが、スレッドの使い方をきちんと理解しておけば、Swing アプリケーションも正しく実装することができます。それではまたお会いしましょう。

Griffon 不定期便 〜第 4 回 スレッド編 その 2 〜15

G*Magazine vol.3

CodeNarcを利用してGROOVYのコード品質を上げる〜第 3 回 ルールのカスタマイズ〜
series03

荒井健太郎

某 IT ベンダーのセキュリティコンサルタント、ソフトウエアをよりセキュアにするために日々精進しています。
テスト系スクリプトは、G* を利用して書いています。

はじめに
静的コード解析はルールに基づいてソースコードを解析することでコードの問題を指摘することができる。

CodeNarc は、 標 準 で 264（2011 年 8 月 現 在 ） の ル ー ル をも っ て い る。 標 準 ル ー ル は、http://codenarc.sourceforge.net/
codenarc-rule-index.html から参照可能である。

今回は、静的コード解析の心臓部ともいえるルールのカスタマ

イズ方法を解説する。

ルールのカスタマイズ概要
ルールカスタマイズは、以下の 2 つの方法がある ( バージョン

0.15 ではカスタマイズ方法が追加されたようだが詳細はまだ調査中のため今回はバージョン 0.13 時点の情報を元に記述 )。
• 標準ルールをカスタマイズする方法• 新しいルールを記述する（標準ルールに存在しないルールを記述する）方法■標準ルールのカスタマイズ

標準ルールのプロパティを変更することによってルールをカス

タマイズする方法である。標準ルールのプロパティ値を XML ファ
イルで指定することによってカスタマイズする。

以下の例は、クラスの大きさをチェックするルールの閾値を500 にカスタマイズしている例である。

各 ル ー ル が 持 っ て い る プ ロ パ テ ィ は、http://codenarc.

sourceforge.net/codenarc-rule-index.html で確認することが可能である。

<ruleset xmlns="http://codenarc.org/ruleset/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://codenarc.org/ruleset/1.0
http://codenarc.org/ruleset-schema.xsd"
xsi:noNamespaceSchemaLocation="http://codenarc.org/ruleset-schema.xsd"><description>Class Size Check Rule</description><ruleset-ref path='rulesets/size.xml'><rule-config name='ClassSize'><property name='maxLines' value='500'/></rule-config></ruleset-ref></ruleset>■新しいルールの記述

新しいルールは CodeNarc のソースコードを変更することで実現する。コードスニペットを生成するスクリプトが用意されているので比較的容易に実現することが可能である。

今回は、try ブロックが空である場合に警告を発するルールを例として記述方法の流れを説明する。
●ソースコードのチェックアウト

ソ ー ス コ ー ド を 変 更 す る こ と に な る の で CodeNarc のSubversion からソースコードをチェックアウトする。以下はチェックアウトコマンドの例である。

svn co https://codenarc.svn.sourceforge.net/
svnroot/codenarc codenarc●ソースコードのビルド

チェックアウトしたソースコードのルートへ移動しソース

コードをビルドする。CodeNarc は Maven で管理されているため
pom.xml があるディレクトリで以下のコマンドを実行するとビルドできる。

mvn install●ルールのコードスニペットを作成

ルートディレクトリの codenarc.groovy スクリプトを実行する。実行後、ルール名等の入力を促されるので入力する。

16 CodeNarc を利用して GROOVY のコード品質を上げる〜第 3 回 ルールのカスタマイズ〜

G*Magazine vol.3

groovy codenarc.groovy create-rule
Enter your name:Kentaro AraiEnter the rule name:EmptyTryBlockEnter the rule category. Valid categories are:basic

●ルールファイルの編集

作 成 さ れ た ./src/main/groovy/org/codenarc/rule/exceptions/

AvoidPrintStackTraceRule.groovy を編集する。

ル ー ル の 実 装 は、AbstractRule を 継 承 す る 方 法 とAbstractAstVisitorRule を継承する方法の 2 つがある。

今 回 は、Groovy AST を 利 用 し て 実 装 さ れ て い るbracesAbstractAstVisitor クラスを利用して実装した。詳細は、http://

concurrency

codenarc.sourceforge.net/apidocs/index.html を参照のこと

designdry

import org.codenarc.rule.AbstractAstVisitor

exceptions

import org.codenarc.rule.AbstractAstVisitorRule

formatting

import org.codehaus.groovy.ast.stmt.

generic

TryCatchStatement

grailsimport org.codenarc.util.AstUtil

imports/**

jdbc* There is empty try block

junit

*

logging* @author Kentaro Arai

naming*/securityclass EmptyTryBlockRule extends

serialization

AbstractAstVisitorRule {sizeString name = 'EmptyTryBlock'unnecessary

int priority = 2

unusedClass astVisitorClass =exceptions

EmptyTryBlockAstVisitor

Enter the rule description:There is empty try block}

Created ./src/main/groovy/org/codenarc/rule/exceptions/
EmptyTryBlockRule.groovy
Created ./src/test/groovy/org/codenarc/rule/exceptions/class EmptyTryBlockAstVisitor extends
AbstractAstVisitor {void visitTryCatchFinally(TryCatchStatement tryCatchStatement) {EmptyTryBlockRuleTest.groovy

if (AstUtil.isEmptyBlock(tryCatchStatement.tryStatement)) {Updated ./src/main/resources/codenarc-base-messages.properties

addViolation(tryCatchStatement)

Updated ./src/main/resources/rulesets/exceptions.xml}

Updated ./src/site/apt/codenarc-rules-exceptions.apt

super.visitTryCatchFinally(tryCatchStatement)

Updated ./CHANGELOG.txt
adding to svn... A

src/main/groovy/org/codenarc/rule/adding to svn... A

src/test/groovy/org/codenarc/rule/

exceptions/EmptyTryBlockRule.groovy
exceptions/EmptyTryBlockRuleTest.groovy
Finished}}●ビルドして実行

再度、ソースコードをビルドして CodeNarc を実行すると try

ブロック内が空の場合に警告があがってくる。

●プロパティファイルの編集●その他

properties を編集する。

わなかったが、テストコードを作成するための便利なユーティ作 成 さ れ た ./src/main/resources/codenarc-base-messages.
このファイルは結果の表示順をコントロールするファイルである。ファイルの先頭に追加された以下の部分を任意の場所 ( 希望する表示位置 ) に移動する。

今回はスニペットとして作成されたテストコードの編集は行リ テ ィ が org.codenarc.rule.AbstractRuleTestCase に 用 意 さ れ てい る。 詳 細 は、http://codenarc.sourceforge.net/apidocs/index.
html を参照のこと。

# todo: manually sort your messages into thecorrect location
EmptyTryBlock.description=There is empty try block
EmptyTryBlock.description.html=There is empty try block

CodeNarc を利用して GROOVY のコード品質を上げる〜第 3 回

ルールのカスタマイズ〜17G*Magazine vol.3■まとめ

今回は CodeNarc のルールを拡張する方法を 2 つ紹介した。標

準ルールのカスタマイズは CodeNarc を実プロジェクトで利用する際に自社の基準と照らし合わせて利用して頂きたい。また、新しいルールを記述することは障害等が発生した場合のパターンをノウハウ ( 形式知化 ) とする場合に非常に威力を発揮する。慣れるまでは記述が煩雑であるがそれ以上の価値があるので是非実施して頂きたい。

今回で CodeNarc を利用した静的コード解析の連載は最終回である。私は、開発プロセスの初期から適用できるコード品質向上策として静的コード解析は非常に有効な手段だと考えている。この連載がコード品質向上の一助になることを願っている。

18 CodeNarc を利用して GROOVY のコード品質を上げる〜第 3 回 ルールのカスタマイズ〜

G*Magazine vol.3

GebではじめるWebテスト〜第２回 Geb0.6 編〜
series04

綿引 琢磨（わたびき たくま）

Groovy/Grails/Griﬀon/Java 界隈のやさぐれエンジニア。
日本 Grails/Groovy ユーザーグループ運営委員。

G* な皆さん、こんにちは。今回は vol.1 で予告した Geb のアー

Configuration クラスおよび ConfigurationLoader クラスを通じてして 6/28 にリリースされたバージョン 0.6 での新機能と変更点をBrowser クラスのコンストラクタやメソッドのインタフェースもキテクチャを全編にわたって紹介する予定でしたが、予定を変更
中心に、より強力になった Geb の魅力についてお伝えしたいと思います。

行うようになりました。これにより、Geb の中心のクラスである
変更されています。設定に関連するクラスは次のような構成になっています。

Geb0.6 の概要
Geb0.6 では多くの機能が追加されただけでなく、既存機能に対しても再実装がなされるなど、大幅な変更が行われました。以
下はマニュアルに記載されている新機能、変更点リストから一部を抜粋したものです。
• 設定の仕組み• レポート機能の主要ドライバーへの対応• 外部スクリプトのバインディング• ダイレクトダウンロード API• タイムアウト待ち時間、リトライ回数の設定機能• ビルドシステムやフレームワークとの統合• Navigator, Module クラスに大きさや位置を取得するための属性追加• TestNG のサポート• Browser#drive() インタフェースの変更• Grails 用のテストクラスの削除

設定はテストコード内に直接記述するか、もしくは外部で指定することで行います。

●テストコード内での設定

テストコード内で設定する場合は、Configuration クラスを生成してプロパティの設定を行い、そのインスタンスを Browser ク

ラスのコンストラクタもしくは Browser#drive() メソッドの引数として渡します。

これら以外にもクラスの振る舞いが変わったものが多数あります。詳細は Geb のマニュアルを参照してください。

では、前述の中から特に注目したい新機能と重要な変更点について見ていきましょう。

注目の新機能今回のバージョンアップで追加された機能には、新たな仕組みを導入して実現したものと既存機能を拡充して実現したものとがあります。本記事では新たに追加された設定の仕組みと、既存機能を拡充したレポート機能、外部スクリプトのバインディングについて解説します。

■ 設定の仕組み

Geb0.6 では新たに設定の仕組みが導入されました。これまで

はベースとなる URL や使用する WebDriver などは、Browser クラスの生成時または実行時に指定するようになっていましたが、今回追加された設定も含め、Geb の動作に関わる設定についてはGeb で始める Web テスト〜第 2 回 Geb0.6 編〜 19

G*Magazine vol.3▼NewGebTest.groovy
package hello▼GebConfig.groovy
import org.openqa.selenium.htmlunit.HtmlUnitDriver
import org.openqa.selenium.firefox.FirefoxDriver

import org.junit.Test

import org.openqa.selenium.chrome.ChromeDriver

import geb.Browser
import geb.Configuration;

driver = {def driver = new HtmlUnitDriver()import pages.*

driver.javascriptEnabled = trueclass NewGebTest {driver}@Test
void helloJGGUG() {environments {// 設定// grails -Dgeb.env=chrome test-app

Configuration conf = new Configuration()

chrome {// 使用する WebDriver の指定

driver = { new ChromeDriver() }conf.setDriverConf('firefox')}

conf.setReportsDir(new File('c:/temp'))// grails -Dgeb.env=firefox test-app// レポート出力ディレクトリの指定

firefox {driver = { new FirefoxDriver() }Browser.drive(conf) {}to HelloPage
assert at(HelloPage)
report 'hello_page'username = 'JGGUG'
greetButton.click()assert at(GreetingPage)
report 'greeting_page'
assert h1Text == 'Hello, JGGUG!'}}}●外部からの設定

外部から設定を行う方法としては、設定スクリプト、システム

プロパティ、BuildAdapter クラスの実装の３つの方法があります。
BuildAdapter クラスの実装による設定は、現状ではベース URL とレポート出力ディレクトリしか指定することはできません。ここでは標準的な設定方法である設定スクリプトによる指定方法について説明します。

設定スクリプトは明示的にパスを指定してクラスローダーから読み込ませることもできますが、通常はデフォルトパッケージ上に GebConfig.groovy という名前の ConfigSlurper スクリプトを用意して自動的に読み込ませます。Grails で使用する場合には test/
functional の直下に、Maven や Gradle などのビルドツールから使用する場合には test/resources の直下に配置すれば良いでしょう。次の設定スクリプトは Grails での機能テスト用のスクリプトです。

20 Geb で始める Web テスト〜第 2 回 Geb0.6 編〜}
この設定ではデフォルトでは HtmlUnitDriver が使用されます。

実行時に geb.env プロパティを指定することで、ChromeDriver

や FirefoxDriver を 使 用 で き ま す の で、 必 要 に 応 じ て 柔 軟 にWebDriver を変更することができます。

■レポート機能前バージョンまでは、統合するテストフレークワークごとに用意されたレポート出力用のテストクラスを継承し、さらに使用する WebDriver が FirefoxWebDriver の場合でしかレポート機能を利用することができませんでした。今回のバージョンからは、専用のテストクラスを継承することなく、Browser クラスから直接レポート出力ができるようになり、出力できる WebDriver にも制約はなくなりました。

また、画面のスクリーンショットに関しては TakesScreenshot

インタフェースを実装する全ての WebDriver で出力されるようになっています。Firefox, Chrome, IE などの標準的な WebDriver は
上記のインタフェースを実装していますので、システム要件で使用ブラウザが制限されている場合でもレポート機能を利用することができます。

レポートを出力するためには、出力先のディレクトリを設定しておく必要があります。設定は前述の Configuration クラス、シ

ステムプロパティ、または BuildAdapter クラスの実装によって行います。

G*Magazine vol.3▼Configuration クラスによる設定

Configuration conf = new Configuration()def conf = new Configuration()conf.setDriverConf('firefox')

conf.setReportsDir(new File('c:/temp'))▼システムプロパティによる設定// レポート出力ディレクトリの指定
conf.setReportsDir(new File('c:/temp'))

System.setProperty('geb.build.reportsDir','c:/temp')

Browser.drive(conf) {to HelloPage
assert at(HelloPage)▼CustomBuildAdapter.groovyclass CustomBuildAdapter implements BuildAdapter// c:/temp/helloJGGUG-1 ディレクトリに出力{

reportGroup 'helloJGGUG-1'
report 'hello_page'

File getReportsDir() {new File('c:/temp')

username = 'JGGUG'}

greetButton.click()}assert at(GreetingPage)

レポートは Browser#report() メソッドで出力します。引数の文字列が出力ファイル名となり、デフォルトでは同名の html ファ// c:/temp/helloJGGUG-2 ディレクトリに出力

イルと png ファイルが出力されます。

reportGroup 'helloJGGUG-2'
report 'greeting_page'// hello_page.html と hello_page.png が出力される
report 'hello_page'
さらに、新機能としてレポートグループディレクトリの指定がassert h1Text == 'Hello, JGGUG!'}

可能になっています。レポートグループディレクトリとは、出力
先ディレクトリのサブディレクトリとして作成されるディレクト

リのことで、出力前にグループディレクトリを指定しておくと、そのディレクトリを作成し、レポートを出力します。

グループディレクトリをテストコード内で適時指定することによって、テストごとや画面単位で出力先を変更できますので、エビデンスの管理がしやすくなるのではないかと思います。

■外部スクリプトのバインディング

もう一つ注目すべき新機能として、外部スクリプトのバイン

ディングを取り上げます。これは EasyB Geb プラグインで実装されていた仕組みを汎用的に使用できるよう抽出した機能です。外部スクリプトとは、スクリプトとして外部に切り出されたテスト
コードをのことを指します。つまり、物理的に別で定義されているテストコードを、実行時に Browser クラスとバインディングさせて、テストを実施することができるのです。バインディングを利用したテストは次のようになります。

Geb で始める Web テスト〜第 2 回 Geb0.6 編〜

21

G*Magazine vol.3▼GebConfig.groovy
driver = "firefox"▼Scenario.groovy
import pages.*

reportsDir = "c:/temp"▼ScriptBindingTest.groovy

to HelloPage
assert at(HelloPage)
reportGroup 'helloJGGUG-1'

package helloreport 'hello_page'

import org.junit.After
import org.junit.Before
import org.junit.Test
import geb.Browser
import geb.binding.BindingUpdater
import groovy.lang.Bindingclass ScriptBindingTest {def binding = new Binding()
def browser = new Browser()def updater = new BindingUpdater(binding, browser)

page.username = 'JGGUG'
page.greetButton.click()assert at(GreetingPage)
reportGroup 'helloJGGUG-2'
report 'greeting_page'
assert page.h1Text == 'Hello, JGGUG!'
この仕組みを利用することによって、これまでは不可能だった

Cuke4Duke（JVM 向けの Cucumber）との統合も可能となりました。これは、単に Geb を UAT に利用する際のテストフレームワー
クの選択肢が増えただけでなく、さらに柔軟な形でテストコード@Beforevoid setUp() {updater.initialize()}を記述できる可能性を得たということになります。

重要な変更点
今回のバージョンアップでは機能が増えたことに伴い、インタ@Testvoid helloJGGUGFromScenario() {new GroovyShell(binding).evaluate(new File("src/test/features/Scenario.groovy"))}

フェースの見直しや振る舞いが変更されたメソッドが多くあります。また依存するライブラリのバージョンも変更になっています
ので、重要なものに絞ってお伝えします。

■ 依存ライブラリのバージョン@Aftervoid tearDown() {updater.remove()}}

Groovy と WebDriver の最小バージョンがそれぞれ引き上げられました。

• Groovy 1.7 以上• WebDriver 2.0rc1
Groovy は最新の 1.8.1 でも特に問題もなく安定して動作していますので、最新バージョンにしておいて問題ないと思いますが、
WebDriver についてはブラウザのバージョンとの兼ね合いもあり、場合によっては使い分けが必要になるかもしれません。筆

者の Windows 環境では InternetExplorerDriver を使用したテスト
で終了時に JVM の致命的エラーが発生する事象も起きています

ので、問題が発生した場合にはブラウザを変更したり、使用する WebDriver のバージョンを変更するなどの対応をおすすめします。

■ インタフェースの見直し

前述の通り、Browser クラスではインタフェースが大きく変わりました。特に Geb のエントリーポイントでもある drive() メソッ

ドでは、Closure を引数とするメソッド以外は変更されています

22 Geb で始める Web テスト〜第 2 回 Geb0.6 編〜

G*Magazine vol.3
ので、注意してください。追加された API と削除された API は次の通りです。

まとめ如何でしたでしょうか？今回のバージョンアップでは、大きく▼Geb0.6 から追加された Browser クラスの API// コンストラクタ

変わりながらも、着実に使いやすくより実用的になったように思われます。レポート機能のディレクトリグループの設定は地味ではありますが、エビデンスを管理する上で非常に助かる機能です
し、外部スクリプトのバインディングは業務シナリオに特化しBrowser() {}たスクリプトを外部に置くことができ、UAT での利用がしやすく

Browser(Configuration config) {}Browser(Map props, Configuration config) {}なったのではないでしょうか。

// drive() メソッド

行する方法についてご紹介したいと思います。お楽しみに！

さて、次回はテストフレームワークと連携して Geb を CI で実

static Browser drive(Configuration conf,
Closure script) {}static Browser drive(Map browserProperties,
Closure script) {}static Browser drive(Browser browser,
Closure script) {}リンク• Geb マ ニ ュ ア ル
index.html

http://www.gebish.org/manual/current/▼Geb0.6 から削除された Browser クラスの API// コンストラクタ
Browser(Class pageClass, Map params = null) {}Browser(String baseUrl, Class pageClass = null,
Map params = null) {}Browser(WebDriver driver = null, String baseUrl =null, Class pageClass = null, Map params = null) {}// drive() メソッド
static drive(Class pageClass, Closure script) {}static drive(String baseUrl, Closure script) {}static drive(String baseUrl, Class pageClass,
Closure script) {}static drive(WebDriver driver, Closure script) {}static drive(WebDriver driver, String baseUrl,
Closure script) {}static drive(WebDriver driver, Class pageClass,
Closure script) {}■ Grails テストクラスの削除

Grails で使用するためのテストクラスとして、以前まではテス

トフレームワークごとにサブクラスが用意されていましたが、今回のバージョンアップでは削除されました。今後はサブクラスの継承元になっていたクラスを直接使用します。
統合する

テストフレームワーク

削除されたサブクラス

継承元のクラス（今後直接使用する）

JUnit 3JUnit3GebTests

geb.junit3.GebReportingTest

JUnit 4

GebTests

geb.junit4.GebReportingTest

EasyB

GebEasybPlugin

geb.easyb.GebPlugin

SPock

GebSpec

geb.spock.GebReportingSpec

Geb で始める Web テスト〜第 2 回 Geb0.6 編〜

23

G*Magazine vol.3

もし新人女子 Java プログラマが『Groovy イン・アクション』を読んだら 〜第２章〜
series

06

吉田

健太郎

日本 Grails/Groovy ユーザグループ運営委員。Groovyエバンジェリスト。
仕事では Java、趣味でライフワークの如く Groovy と戯れるプログラマ。
ブログ：『No Programming, No Life』http://d.hatena.ne.jp/fumokmm/』

第２章 も かは『プログラミング Groovy』と出会った●前回までのあらすじ

プログラマを志望して４月から新人プログラマとして駆け出した七海 萌香 ( ななみ もか ) は同期である一ノ瀬 小夏 ( いちのせ こなつ ) とプログラム研修の日々を過ごしていた。

ある日、こなつと共に書店に立ち寄ったもかはそこで Groovy
イン・アクションと運命の出会いを遂げる。

Groovy イン・アクションを手に入れたもかはその内容に夢中になり Groovy の世界へと引き込まれてゆくのであった。
「よーし、こないだの結果を返すぞ～。」「七海さん、君は名前を間違ってたぞ？仕事でこんなじゃ困る

こなつ「研修が終わっちゃうとみんなバラバラのプロジェクト配属になるんですよね。私、もかちゃんと一緒がいいです」
もか「そうだね、私もこなつと一緒になれるといいなぁって思うよ」
こなつ「佐々木君と、松上君の二人はどうもスラッツワン？っていう Java のワークフレームを使ったプロジェクトに配属になるらしいんですよ」
Struts のバージョン1のことかな？あと多分ワーもか「ええと、クフレームじゃなくて、フレームワークだよ」
こなつ「えっ ... ＞＜ 私全然間違えちゃってますか？ はずかしいです ...」
こなつは顔を真っ赤にしている。

んだがな ...」

もか「え？どれだろう？ああっ ... コメント部分かぁ＞＜」* @author 七海 萌花
もか「もぅ、IME のバカバカ！嫌い！もかって名前はいつも一発じゃ変換できないんだよねぇ ... 今度から気を付けな
くっちゃ」

■お昼休み

ランチタイムの喫茶店は今日も混み合っていた。もかとこなつ

は会社近くのカフェにランチに来ていた。テーブルにはパスタとパニーニ、それからアイスコーヒー、エスプレッソなどが並んでいる。

もか「はぁ ...」

こなつ「どうしたんですか、もかちゃん。ため息ですか？」

もか「あ、うん、今日のコメントのやつなんだけどさぁ ...」

こなつ「そんなの気にすることないですよ、もかちゃんの書いたコードが凄すぎるからみんな嫉妬してるんですよ」
もか「えっ、そんな、すごいなんてことはないよ」

こなつ「ううん、そんなことあります。もかちゃんは私の目標なんです」
もか「ええっ？目標ってそんな ...」
もかは照れ笑いを浮かべる。

24 もし新人女子 Java プログラマが『Groovy イン・アクション』を読んだら 〜第 2 章〜

G*Magazine vol.3
もか「まぁ ... 分かりにくい名前だし、しょうがないよ」

こなつ「うわーん、もかちゃん優しいです ... ぐすん」

もか「それにしてもあの二人、もう配属先決まってるんだ、やるなぁ」

こなつ「確か ... 出来がいいから、クライアントから早めのオファーがもらえたってことらしいですよ」
もか「ふーん」

こなつ「でも、あの二人、JDK のバージョンがいまだに 1.4 とかありえなくない？とかそんな話をしてたんですけど、私ついていけなくて ...」
もか「あぁ、Struts ってたしか結構古いフレームワークなん
だよね、この業界、最新ばかり追えるわけじゃないんだ「@groovybook G マガ読んだよ！ http://grails.jp/g_mag_jp/」って「ついっといったー（注釈：ひとことつぶやきサービス）」

でつぶやくと本を抽選でプレゼントするっていう企画があったみたいなんです。イベントに参加していなくても応募資格があるってことでしたので、私もちょっとつぶやいてみたんです。そうしたら当たっちゃいました！えへっ」（注釈：こなつと同じように実際にみなさんも Twitter でつぶやいて下さい。抽選で１名様に『プログラミング Groovy』をプレゼント致します。当選者発表は 10 月開催予定の G* ワークショッ
プで！）ぜって口癖の人を一人知ってるわ」
こなつ「最新どころか基礎の基礎の基礎にもついて行けない私は
どうしたらいいんですか＞＜」
もか「まぁまぁ ... 頑張ろうよこなつ！まだ始まったばっかり

こなつはプログラミング Groovy を自分の顔の横に持っきて、満面の笑みを浮かべている。

なんだし」
こなつ「ありがとうございます。やっぱりもかちゃんは優しいです」
感情のころころ変わる子だなぁ、まぁそこがかわいいんだけど。
こなつ「あっ！そういえば話変わりますけど、もかちゃん、あの日、本屋さんで見てた厚い本って結局買ったんですよね？すごいです」
もか「うん買ったよ。Groovy イン・アクションのことでしょ？
ちょっとお財布は痛かったんだけどね（涙）」こなつは目を輝かせてもかに話かけてくる。くりっとした目が小動物のようで、例えるならリス？

こなつ「あれってジーナちゃんですよね！私あの後、色々調べたんですよ。Groovy イン・アクションって海外とかでは
GinA って略されてて、それで、ジーナって呼ばれてるらしいんです」ジ、
もか「え、そっ、そうなの？じーいんえーって略すんだ？ で、ジーナ？そうなの？またどうして調べたりなんか ... ？」話の展開が急すぎてもかは頭がついていっていない。しかしお構いなしにこなつは続ける。

も か「 待 っ て 待 っ て ＞ ＜ え え と ま ず『 プ ロ グ ラ ミ ン グ
Groovy』っていう本を私知らないんだけど ...」
こなつは微笑んでこうつぶやく。
こなつ「ペンギン本です」

もか「ペ、ペンギン ... 本？」

こなつ「はい、ジーナちゃん以来、国内では Groovy の書籍が出

こなつ「ジーナちゃんって名前はかわいいと思いませんか？だか
らそう呼ぶことにしたんです。色々調べ始めたのはもかちゃんが私の目標だからです」てなかったんですけど、今回満を持して発売された本らしいんです。Groovy の最新版に対応しているらしいんですよ。それから真ん中に可愛いペンギンさんが付いてますよね？ほら、だから本を買ったみんながペンギン

目の輝きが先ほどよりも明らかに増している。この子ってもしかして、何かに夢中になるとすごい力を発揮するタイプなの？？
こなつ「それから聞いて下さい！実は、つい最近なんですけど、本って愛称を付けて呼んでるんですよ。かわいいですよね。青本って呼ぶ人もいるみたいですけど、私は断然、
ペンギン本です！」
もか「へぇ ... 私 Groovy イン ... あっ、ジーナ ... ちゃん？ばっとある会場で『プログラミング Groovy』の著者の先生たちのイ

かり読んでて Groovy が今どうなってるのかってあまりて、ナちゃんが書かれたのって結構前らしくて、Groovy 最

ベントがあったみたいなんですよ。そのイベントの目玉企画とし気にしてなかったなぁ ...。そういえばバージョンもジー

もし新人女子 Java プログラマが『Groovy イン・アクション』を読んだら 〜第２章〜

25

G*Magazine vol.3
新版の機能とかまだ追いついてないところがあるみたい

もか「ナナ ... ハヤ？んーなんかどっかで聞いた事あるようだし ... そのまま書いても動かないところがあったりでちょっと困ってたところなんだよね」

な ...」
こなつ「え？そうなんですか？そういえばもかちゃんもついっと
いったーやってるんですか？もしよかったら ID 教えて

こなつ「そういうことなら！ペンギン本は最新版の Groovy にも対応しているらしいんですよ。たしかバージョン 1.8 ？

欲しいです」
もか「あぁ ... 昔登録した ID ならあるよ。今は全然やってはいだったと思います」
もか「ふぇー、すごいわ。ちょっと読ませてもらってもいい？」

こなつ「もちろんです」

ないんだけどね。そっかぁ、こなつがやってるんなら私も再開しようかな」
こなつ「うん！一緒にやりましょう！もかちゃんもやってくれたら楽しいと思います。ナナハヤさんも紹介しますよ！ナ

もかはこなつからペンギン本を受け取る。目をぐっと引く深い

ナハヤさん、Groovy のことも詳しいみたいなんですよ、

青の表紙、真ん中のペンギンちゃん、右下には Java エンジニア必携！の文字もある。中身をパラパラっとめくってみる。

P80 では範囲の説明。なんだか図解がキレイでわかりやすいか

も ...。P144 には SwingBuilder で作った TODO リストアプリとかが載ってる。面白そうなアプリケーション。
...
...
...

（１分後）
もか「こなつ！ねぇ、また今日の夕方、本屋さんまで付き合ってくれる？ 私もこれ欲しい！」
こなつ「もちろんいいですよ」

だからもかちゃんにもぴったりかなぁって思うんですよね」
もか「なるほどね、そのナナハヤさんって人にペンギン本のこととか、イベントのこととか教えてもらったってことね？ ... でもネットの人ってなんか怖くない？」
こなつ「こわい ... ？そうですか ... ？」この子は ... 人を疑うことを知らないのかしら。
もか「だって、どこに住んでるとか、何やってるとかぜんぜん
分からないでしょ？こなつは怖くないの？」
こなつ「ナナハヤさんは、都内で SE をやってるらしいんですよ、
歳は私たちよりも 10 くらい上みたいです。SIer さんらしいんです。お仕事はいつも大変そうですけど。なんだ■ファミレス

こうして、その日のうちにもかはプログラミング Groovy も購入することになった。興奮冷めやらぬ中、二人は今度は会社近くのファミレスで一緒に夕食を取ることになった。料理を注文してか同じ業界の先輩っ！て感じで、全然怖くないですよ」
もか「えっ ... それって ... もしかして、まってまって！」

こなつ「ん？ ... えっ？ ... ち、違いますよぉ ... 別にその、好きとかそんなんじゃぁ ...」

から届くまで、もちろん話題はプログラミング Groovy のことで持ち切りだった。

もか「うわー、この本、すごく読みやすいね！ジーナも丁寧な文章で読みにくくはないんだけど ... ちょっと専門的すぎて＞＜ 読み飛ばしちゃってるところもあったりするの」
こなつ「そうなんですね、私は逆にジーナちゃんを読んでないからわからないんですけど ... でも購入したみなさんは、もか「都内で、SIer で、ナナハヤ ... 七早 ...、七海 早瀬！」

こなつ「え？ あっ ...（なんだぁ私の思い過ごしかぁ＞＜）」

こなつ「ええと ... ななみ はやせ、さんっていうのは、どちら様なんですか？」
もか「... うちの兄貴 ...」

こなつ「ええっ？？？ お、お兄さん？」

もか「うん、私、歳の離れた兄貴がいるんだけど、兄貴は東京で一人暮らししてる。最近ずっと会ってないけど会社はすごくいい本だって言ってます。私もせっかく当たった

いわゆる SIer だね。ついっといったーやってるかどう

し、もかちゃんが気に入っている Groovy だし、頑張っかは知らないんだけど、ナナハヤって ID からすると兄て読み始めたところなんですけど ...Java が分かってな

貴なんじゃないかなぁ ...」

いからちょっとやっぱり難しいです、でも頑張ります！」
もか「ねぇ、こなつさぁ、そういえばちょっと気になってたことがあるんだけど、きいていい？」
こなつ「え？はい ... どうぞ」

もかは浮かない顔をしている ... こなつは心配になって声をかける。

もか「えっと、こなつはどこでこのペンギン本を知ったの？私なんて全然知らなかったよ」

こなつ「えっと ... はい、私のついっといったーのフォロワー
さんで、ナナハヤさんって人がいるんですけど、私がJava 分かんないよー＞＜ってつぶやいてたら優しく教えてくれたので、それ以来、仲良くさせてもらってるんです」

26 もし新人女子 Java プログラマが『Groovy イン・アクション』を読んだら 〜第 2 章〜

G*Magazine vol.3
こなつ「それなら、直接聞いてみたらわかるんじゃないですか？」
もか「わかんないよ、電話番号とか、メールアドレスとか知ら
ないし」

■帰り際

もか「あぁやっちゃったぁ私 ... 明日こなつにちゃんと謝らな
きゃなぁ ...」

こなつ「ええっ ...、兄妹なのになんでですか？」
こなつに質問されるともかはちょっとむっとした顔をした。こなつは困惑する。

もかは二人兄妹で、早瀬という名の 10 歳上の兄がいる。
兄は小さい頃からコンピュー

タに興味を持っていて、特にプログラミングが好きだった。そんな兄の影響で、もかは小さい頃から兄にプログラ

ミングを教えてもらっていた。ただ、やはり幼いもかには理解できない部分が多かったため、なんとなく流れは理
解できるものの、胸を張ってプログラミングできるレベルにはなれていなかった。ただ漠然とプログラミングって面

白いなと思うようにはなっていた。

ある日、あのことがあるまではもかはプログラミングが好きな女の子だった。

■自室にてお風呂から上がり、パジャマに着替えて自室の机にて。もかは

自分のノート PC を開いた。傍らには今日買ってきたペンギン本。
もか「さぁ、色々考えたってしょうがない！せっかく買ったん
だし、ペンギン本を読もうじゃないか！」
こなつ「ああーっ、ええっと ... ごめんなさい、なにかあるんですよね ... よ、余計なことを聞いちゃいました私 ... ごめんなさい、ごめんなさい、ごめんなさい ...」
こなつはずっと頭を下げたままずっと謝り続けている。
もか「ごめんね、こなつは何も悪くないよ。それにさ、本当に帯に Hello World のコードがある。
String.metaClass.hello = { "Hello, $delegate!" }println "Groovy".hello()
もか「String のメタクラスに hello ってメソッドを追加していて ... えっと ...hello メソッドは

そのナナハヤさん？が兄貴かどうかもわからないし」の内容

こなつ「ご、ごめんなさい＞＜」"Hello" + 文字列自身を返却するのね。それで、println で、文字列"Groovy" の hello メソッドを呼んでるんだから、えっ

もか「いいって、...」と ... わかったわ！ "Hello, Groovy!" って出力されるのそう言ってから沈黙がどのくらい続いただろう ... その空気を破るようにもかは立ち上がった。

ね、なるほどぉ ... いきなり捻った内容だわ」
もか「なんだかジーナを最初に買った日と同じわくわくどきど
きだわ！」

もか「ごめん、今日は帰るね、本ありがとう。また明日ね」
そう言うともかは足早に席を立ち、自分の分の精算を済ませ、どんどんページをめくって読み進めていくと、P10 で目が止まる。

ファミレスを後にした。

もし新人女子 Java プログラマが『Groovy イン・アクション』を読んだら 〜第２章〜

27

G*Magazine vol.3
もか「Groovy ってやっぱりすごいのね、Groovy は Java を「1.3. Groovy の使いどころ：７つの導入パターン」

周りから助ける最高のパートナーみたいな言語なんだわ。ビルダーとかを使えば今研修でやっている Java をそこには Groovy の特徴を伝える刺激的な７つの言葉が並べられていた。

万能接着剤 (Super Glue)

やわらかな心臓部 (Liquid Heart)
内視鏡手術 (Keyhole Surgery)

スマート設定 (Smart Configuration)

無制限の開放 (Unlimited Openness)

小人さんスクリプト (House-elf Scripts)
プロトタイプ (Prototype)

コンパイルするために書いている Ant のファイルとか、
JUnit を実行させたり、本当に色々できるんだ ...」
気がつくともう深夜 0 時をまわっている。『プログラミング

Groovy』、面白くてもっと読んでいたいけど ...

もか「明日もお仕事だからあまり夜更かしは良くないわ。こな
つにも明日はちゃんと謝ろう。今日は色々あって疲れちゃった。もう寝よう。兄貴、今ごろ何してるのかな ... おやすみなさい」（続く ... ）イラスト・けんやⓒ28 もし新人女子 Java プログラマが『Groovy イン・アクション』を読んだら 〜第２章〜

G*Magazine vol.3

GBench in a Nutshell
永井 雅人（ながい まさと）
プログラマ。GBench, Gj の作者。
最近は Groovy 及びその周辺プロジェクトへの貢献が日課。
共著『パーフェクト Java』

GBench とは何か？

GBench を手に入れる

GBench は Groovy の 為 の 強 力 な ベ ン チ マ ー キ ン グ フ レ ー ム

ワークです。どのぐらい強力か、まずはその一片をお見せしましょう。例えばあなたは今ボトルネックになっているかもしれない次のメソッドのベンチマークを取りたいとします :def doComplexTask() {GBench をインストールする最も手軽な手段は Grape を使うことです。

@Grab(group='com.googlecode.gbench',
module='gbench',version='0.2.0')
しかし Groovy 使いの嗜みとしてここは最初からクラスロー// ...
}

ダ に 読 み 込 ま せ て お く べ き で し ょ う。[GBench プ ロ ジ ェ ク トそういった場合、通常は処理の前後にベンチマーク用のお決ま$GROOVY_HOME/lib に入れてください。

りの処理を書き足すことになります。これはとても退屈な作業です。しかし、GBench を使えば既存のコードに変更を加える必要はありません。あなたがすべきことはたった 1 つ、アノテーショ
ンを付けるだけです :Benchmark AST Transformation
Benchmark AST Transformation は GBench の目玉機能です。既存のコードの変更を必要とせずベンチマークを取得できます。

@Benchmark
def doComplexTask() {■対象範囲の指定// ...

範囲の指定方法は簡単です。対象としたい場所にアノテーショ}
これだけでメソッドの実時間と CPU 時間が出力されます :java.lang.Object doComplexTask()|http://code.google.com/p/gbench/] で jar を 手 に 入 れ、 そ れ を

user:15600100

ンを付けるだけです。

メソッドを対象にする :import gbench.*system:46800300 cpu:62400400 real:396709428
こ れ は GBench の Benchmark AST Transformation と 呼 ば れ るclass Klass {@Benchmark

機能です。どうです、強力でしょう？しかし GBench の機能はdef foo() {これだけではありません。Builder 構文を使って積極的なベンチマークを簡単に実現する BenchmarkBuilder もあります。GBenchが Groovy の特性である AST Transformation と Builder 構文を生かしてることに注目してください。これは非常に重要な事実です。
GBench を使えば Groovy のパフォーマンス向上の実証と一緒にGroovy が如何に強力な言語であるかを知らしめるられるからで

}
}

Klass

java.lang.Object foo()

user:15600100

system:46800300 cpu:62400400 real:396709428

す。

GBench in a Nutshell

29

G*Magazine vol.3
クラスが持つメソッド全てを対象にする :●出力形式の指定

import gbench.*defaulthandle" に出力用の式を指定します。例えば次のようなス出力形式を変更したい場合、システムプロパティ "gbench.

プレッドシート用に CSV 形式で出力したいとします :@Benchmark

class

methoduser

systemcpu

real

def foo() {Klassjava.lang.Object foo()

1560010046800300

62400400

396709428

}

Klass

java.lang.Object bar()

842405400

15600100

858005500

986409515

class Klass {def bar() {}その場合次のように指定して実行します。ベンチマーク情報を

def static baz() {取得する為に使えるバインド変数は klass( クラス名 )、method( メ}ソッドシグネチャ )、time( 時間 ) の 3 つです。time は各時間をプ}

ロパティとして持ちます :Klassjava.lang.Object foo()

user:15600100

system:46800300 cpu:62400400 real:396709428
Klass

java.lang.Object bar()user:842405400

shell> groovy -e "println('class,method,user,
system,cpu,real')" > benchmarks.csv
shell> groovy -Dgbench.defaulthandle="println([klass,method,time.user,time.system,

system:15600100 cpu:858005500 real:986409515
Klass

java.lang.Object baz()time.cpu,time.real].join(','))" MyApp.groovy >>user:31200200

benchmarks.csv

system:0 cpu:31200200 real:39076475
Groovy 1.8 で追加された ASTTransformationCustomizer を使え

ば、プログラムの全てのメソッドのベンチマークも取得できます（但し、ベンチマーク対象のメソッドの呼び出しが入れ子になると、呼び出し側のベンチマーク結果にちょっとしたオーバーヘッドが含まれます）。

// BenchmarkGroovyc.groovy
import gbench.Benchmark
import org.codehaus.groovy.control.
CompilerConfiguration
import org.codehaus.groovy.control.customizers.

特定範囲の出力形式を変更したい場合、出力用の式を含むク

ロ ー ジ ャ を @Benchmark に 渡 す の が 最 も 簡 単 な 方 法 で す（ ※Groovy 1.7 以前では使えません）:@Benchmark({println("${method} of ${klass}:${(time.real/1000000) as long} ms")})def foo() {}も う 少 し 複 雑 な 処 理、 あ る い は Groovy 1.7 以 前 の 場 合 はBenchmarkHandler インターフェイスを使います。実装クラスにgetInstance() メソッドが必要な点に注意してください :ASTTransformationCustomizer

class MyHandler implements Benchmark.

import org.codehaus.groovy.tools.FileSystemCompiler

BenchmarkHandler {static def instance = new MyHandler()static MyHandler getInstance() {def cc = new CompilerConfiguration()

instance

cc.addCompilationCustomizers(}

new ASTTransformationCustomizer(Benchmark))void handle(klass, method, time) {new FileSystemCompiler(cc).commandLineCompile(args)

println("${method} of ${klass}: ${(time.
shell> groovy -cp gbench-0.2.0.jar
BenchmarkGroovyc.groovy MyApp.groovy
shell> java -cp .;%GROOVY_HOME%\embeddable\
groovy-all-1.8.0.jar;gbench-0.2.0.jar MyApp

30 GBench in a Nutshell

real/1000000) as long} ms")}}

G*Magazine vol.3
シングルトンなのであれば次のようにも書けますね :def methods = [@Singleton

foo: {

class MyHandler implements Benchmark.

...

BenchmarkHandler {},void handle(klass, method, time) {bar: {

println("${method} of ${klass}:...

${(time.real/1000000) as long} ms")},}

baz: {}...

後はクロージャと同じように @Benchmark へ渡すだけです :@Benchmark(MyHandler.class)}]methods.each { name, method ->def time = 0

def foo() {(100 + 1).times { n ->}def b = System.nanoTime()

ここまでは便宜上 println による出力例のみをあげてきました

method()が、選択肢は様々です。いくつか例をあげておきます :def a = System.nanoTime()
if (n > 0) { // ignore dry run@Logtime += (a - b)

@Singleton}class MyHandler implements Benchmark.BenchmarkHandler {}void handle(klass, method, time) {log.info(...)}

println String.format('%-20s\t%10d', name, time)}
同じものを BenchmarkBuilder を使えば次のように簡潔に書け}ます（BenchmarkBuilder は初期状態でウォームアップ用に各処@Singletonclass MyHandler implements Benchmark.BenchmarkHandler {理を 1 度空実行します）:
import gbench.*def file = new File('benchmark.log')
void handle(klass, method, time) {def benchmarks = new BenchmarkBuilder().run repeat:100, {file << ...

foo {

}

...

}

}
bar {

BenchmarkBuilder...
}

Benchmark AST Transformation がプログラムの既存コードのベ

baz {ンチマークを取るのに適しているのに対し、BenchmarkBuilder...

はベンチマークプログラムを書くのに適した機能です。例えば同じ用途の API が複数あり、それらのベンチマークを比較したいとします。通常は次のように書くことになります（好みによって多少の違いはあるでしょうが）：}}
benchmarks.prettyPrint()
Benchmark AST Transformation 同様、結果には CPU 時間も含まれます。

GBench in a Nutshell

31

G*Magazine vol.3

user

system

cpu

real

foo

65000000

20000000

80000000

110000000

bar

50000000

10000000

60000000

90000000

baz

55000000

15000000

70000000

100000000

ソートも可能です。例えばユーザ時間でソートしたい場合は次のようにします。run() の戻り値はプロパティとして label と time
を持つオブジェクトのリストです :benchmarks.sort({it.time.user}).prettyPrint()

user

system

cpu

real

bar

50000000

10000000

60000000

90000000

baz

55000000

15000000

70000000

100000000

foo

65000000

20000000

80000000

110000000

合計時間ではなく平均時間を取りたい場合は引数に average:true を追加します :new BenchmarkBuilder().run average: true, repeat: 100, {あ る い は、run() の 代 わ り に sum()、average() を 呼 び 出 せ ば

average 引数を省略できます :new BenchmarkBuilder().sum repeat: 100, {new BenchmarkBuilder().average repeat: 100, {BenchmarkBuilder のオプションはこれらだけではありません。

全てのオプションを知るには javadoc を参照して下さい。

最後にGBench へのフィードバックはプロジェクトの issue tracking

system、作者のブログ、または Twitter:@nagai_masato 経由で送ることができます。 あなたのフィードバックが GBench の改善を継続する力になります。

32 GBench in a Nutshell

G*Magazine vol.3

Grails Plugin 探訪
第4回~ Cloud Foundry プラグイン ~URL: http://www.grails.org/plugin/cloud-foundry
プラグインのバージョン : 1.0.1

対応する Grails のバージョン : 1.3.3 以上

杉浦孝博
最近は Grails を使用したシステムの保守をしている自称プログラマ。
日本 Grails/Groovy ユーザーグループ事務局長。

共著『Grails 徹底入門』、共訳『Groovy イン・アクション』

はじめに今回ご紹介する Grails プラグインは、Cloud Foundry プラグインです。

本記事は、次の環境で動作確認をしております。
• OS : Mac OS X 10.6.8

Cloud Foundry プラグインとはCloud Foundry プ ラ グ イ ン と は、Grails ア プ リ ケ ー シ ョ ン を

Cloud Foundry 上にデプロイしたり、更新したり、また Cloud

Foundry 上の Grails アプリケーションをモニタリングするのを、容易に実行するためのプラグインです。

"vmc" という Ruby で作成されたコマンドラインのツールがあ• Java : 1.6.0_26

りますが、Cloud Foundry プラグインをインストールすることで、

なお、コマンドの実行結果については、紙面の都合上、出力結

Cloud Foundry へのサインイン• Grails : 1.3.7

果を省略しており、実際の出力異なる場合があります。ご了承願います。

Cloud Foundry とはCloud Foundry と は、VMware 社 が 提 供 す る PaaS プ ラ ッ ト

フォームです。

Spring を使った Java アプリケーション、Rails や Sinatra を使っ"vmc" とほぼ同じ機能を実行することができます。

Cloud Foundry を利用するには、サインアップする必要があります。サインアップは、ここから行えます。

サインアップで気を付けて欲しいのは、すぐに Cloud Foundryが利用できるようになるのではなく、利用可能になったことを知らせる招待メールが来るまでしばらく待たないといけない、ということです。

筆者の場合、招待メールが来るまで、3 週間くらいかかりました Ruby アプリケーション、Node アプリケーション、Grails や

た。

します。

サンプルアプリケーションの作成

Scala-Lift など、JVM 上で動作するアプリケーションをサポート
また、MySQL や Redis、MongoDB といったデータベースサー

ビスも提供します。

詳しくは、Cloud Foundy を参照してください。

サンプルアプリケーションとして、今回は書店用のアプリケー

ションを作成します。

$ grails create-app store
ドメインクラスとして、書籍を表す Book ドメインクラスを作成します。

$ cd store$ grails create-domain-class Book

Grails Plugin 探訪

第 4 回〜 Cloud Foundry プラグイン〜

33

G*Magazine vol.3
Book ドメインクラスのコードは次のとおりです。
ackage store

grails.plugin.cloudfoundry.datasource.
disableTimeoutAutoconfiguration

class Book {String title

MySQLデータソースのコネクションタイム
アウトチェックの自動設定を無効にするかど
うか

true

最 低 限、grails.plugin.cloudfoundry.username と grails.plugin.

int pricecloudfoundry.password を設定してください。

String isbn13

た だ し、grails.plugin.cloudfoundry.username と grails.plugin.

cloudfoundry.password に つ い て は、$HOME/.grails/settings.

static constraints = {groovy に記述することもできます。

title(blank: false)
price()

初期状態

isbn13()}

初期状態では、Cloud Foundry 上で利用するサービスやアプリ}

ケーションの設定は何も行われていません。

コントローラとビューは、generate-all コマンドでスカッフォー

ルドします。

cf-services コマンドを利用し、利用できるサービスを確認してみましょう。

$ grails generate-all store.Book$ grails cf-services

Cloud Foundry プラグインのインストール
create-app コマンドで Grails アプリケーションを作成した後、最初に行うことは Cloud Foundry プラグインをインストールすることです。

============== System Services ==============+------------+---------+-------------------------------+| Service| Version | Description|+------------+---------+-------------------------------+$ grails install-plugin cloud-foundry設定| mongodb| 1.8| MongoDB NoSQL store| redis| 2.2| Redis key-value store service || rabbitmq| 2.4| RabbitMQ messaging service|| mysql| 5.1| MySQL database service|| PostgreSQL database service|| postgresql | 9.0

Cloud Foundry を 利 用 す る た め の 設 定 は、grails-app/conf/|+------------+---------+-------------------------------+

Config.groovy に記述します。

設定できるプロパティは、次のとおりです。

=========== Provisioned Services ============

プロパティ名説明

grails.plugin.cloudfoundry.username

アカウント用のユーザ
名/メールアドレスなし(必須)

アカウント用のパス
ワードなし(必須)

grails.plugin.cloudfoundry.target

APIサーバ名'api.cloudfoundry.com'

grails.plugin.cloudfoundry.appname

アプリケーション名

Grailsアプリケー
ションの名前grails.plugin.cloudfoundry.showStackTrace

コンソールにスタックトレースを表示するか
どうか

false

grails.plugin.cloudfoundry.testStartWithGet

GETリクエストで開始や再起動をテストする
かどうか

true

上記プロパティがtrueの場合に使用するURL

アプリケーションのURL

grails.plugin.cloudfoundry.password

grails.plugin.cloudfoundry.testStartGetUrl

デフォルト値

None provisioned yet.
cf-services コマンドを実行すると、利用可能なサービスの一覧と、作成したサービスのインスタンスの一覧が表示されます。

初期状態なので、作成したサービスのインスタンスの一覧には何も表示されません。

また、cf-apps コマンドを利用し、デプロイ済みのアプリケー

ションの一覧を見てみましょう。
$ grails cf-apps
No Applications

初期状態なので、デプロイ済みのアプリケーションの一覧には

34 Grails Plugin 探訪 第 4 回〜 Cloud Foundry プラグイン〜

何も表示されません。

最後に、cf-info コマンドを利用し、リソースの使用状況を見て

G*Magazine vol.3

アプリケーションのデプロイ

みましょう。

サービスのインスタンスを作成したら、サンプルアプリケー$ grails cf-info

ションをデプロイしてみましょう。

VMware's Cloud Application Platform
For support visit http://support.cloudfoundry.com
Target:アプリケーションのデプロイは、cf-push コマンドを使用し、引数として先ほど作成したサービスのインスタンスを指定します。

http://api.cloudfoundry.com (v0.999)$ grails prod cf-push --services=mysql-a366716
User:

your.email@yourserver.com

Usage:

Memory( 省略 )(0B of 2.0G total)[jar] Building jar: /Users/rhapsody/

Services (0 of 16 total)
Apps

Project/gmaga/plugin004/store/target/cf-(0 of 20 total)

temp-1314297372348.war[delete] Deleting directory

サービスのインスタンスの作成/Users/rhapsody/.grails/1.3.7/projects/store/stage
Done creating WAR /Users/rhapsody/

Cloud Foundry 上のサービスを利用するためには、サービスのProject/gmaga/plugin004/store/target/cf-

インスタンスを作成する必要があります。

サービスのインスタンスの作成は、cf-create-service コマンドで行います。

今回は MySQL を利用するため、MySQL のサービスのインスタンスを作成することにします。

temp-1314297372348.war
Application Deployed URL: 'store.cloudfoundry.com'?
途中で、アプリケーションをデプロイする URL を指定します。
デフォルトでは、アプリケーション名 .cloudfoundry.com とな

りますが、別の URL することもできます。

$ grails cf-create-service mysql

ここでは、そのまま (store.cloudfoundry.com) にしてみます。

Service 'mysql-a366716' provisioned.
再度 cf-services コマンドを実行すると、MySQL サービスのイ

ンスタンスが作成されていることを確認できます。

com.vmware.appcloud.client.CloudFoundryException:============== System Services ==============

400 Bad Request (The URI: "store.cloudfoundry.

+------------+---------+-------------------------------+| Version | Description|+------------+---------+-------------------------------+| redis| 2.2| Redis key-value store service || mongodb| 1.8| MongoDB NoSQL store|| rabbitmq| 2.4| RabbitMQ messaging service|| postgresql | 9.0| PostgreSQL database service|| mysql| MySQL database service

|

| 5.1

com with 512MB and services [mysql-a366716]:
Error: 400 Bad Request$ grails cf-services| Service

Creating application store at store.cloudfoundry.

+------------+---------+-------------------------------+( 省略 )

com" has already been taken or reserved)

どうやら、store.cloudfoundry.com は既に存在するようで、別の URL にする必要があるみたいです。

$ grails prod cf-push --services=mysql-a366716( 省略 )
Application Deployed URL: 'store.cloudfoundry.com'?

=========== Provisioned Services ============+---------------+---------+| Name| Service |+---------------+---------+| mysql-a366716 | mysql|+---------------+---------+

Grails Plugin 探訪

第 4 回〜 Cloud Foundry プラグイン〜

35

G*Magazine vol.3
今 度 は、 デ プ ロ イ 先 の URL と し て jggugstore.cloudfoundry.

com と入力します。

▼実行例$ grails cf-help

jggugstore.cloudfoundry.com
General syntax: grails [environment] <scriptName>
Creating application store at jggugstore.

[<args>] [command_options]cloudfoundry.com with 512MB

Use 'grails help <scriptName> for more

and services [mysql-a366716]: OK

information.

Uploading Application:

Currently available commands are:Checking for available resources: OK
Getting Started

Processing resources: OK

cf-info

Packing application: OK

System and account information

Uploading (257K): OK
Applications
cf-appsTrying to start Application: 'store'.
......

List deployed applications( 途中省略 )Application 'store' started at http://jggugstore.cloudfoundry.com
ここまで表示されれば、アプリケーションのデプロイは成功で

す。

Help
cf-help

Get general help

Web ブラウザで http://jggugstore.cloudfoundry.com/ にアクセスすると、スカッフォールドした画面が表示されます。

■インフォメーション●cf-info

では、cf-apps コマンドを再度実行してみましょう。

Cloud Foundry のリソース使用状況を表示します。

▼使用方法$ grails cf-apps+-------------+----+---------+-----------------------------+---------------+| Application | # | Health | URLs

| Services|+-------------+----+---------+-----------------------------+---------------+| store| 1 | RUNNING | jggugstore.cloudfoundry.com | mysql-a366716 |+-------------+----+---------+-----------------------------+---------------+
デプロイしたアプリケーションの状態を確認することができます。

cf コマンド
アプリケーションのデプロイまで解説してきましたので、ここから残りは、Cloud Foundry プラグインが提供するコマンドを説$ grails cf-info▼実行例$ grails cf-info
VMware's Cloud Application Platform
For support visit http://support.cloudfoundry.com
Target:http://api.cloudfoundry.com (v0.999)

User:

your.email@yourserver.com

Usage:

Memory明します。

■ヘルプ●cf-help

使用できるコマンドの一覧を表示します。

▼使用方法$ grails cf-help

Apps(1 of 20 total)■アプリケーション●cf-apps

アプリケーションの一覧を表示します。

▼使用方法$ grails cf-apps

36 Grails Plugin 探訪 第 4 回〜 Cloud Foundry プラグイン〜(512.0M of 2.0G total)

Services (1 of 16 total)

G*Magazine vol.3
▼実行例

▼実行例
$ grails cf-start$ grails cf-apps+-------------+----+---------+-----------------------------+---------------+

Trying to start Application: 'store'.

| Application | # | Health | URLs......

| Services|+-------------+----+---------+-----------------------------+---------------+| store| 1 | RUNNING | jggugstore.cloudfoundry.com | mysql-a366716 |+-------------+----+---------+-----------------------------+---------------+

Application 'store' started at http://jggugstore.cloudfoundry.com●cf-stop

アプリケーションを停止します。

■デプロイ

▼使用方法

●cf-push

war を作成しアプリケーションをデプロイします。

▼使用方法$ grails cf-stop [--appname= アプリケーション名 ]▼実行例$ grails cf-push [--appname= アプリケーション名 ][--url=URL] [--memory= メモリサイズ ][--warfile=war ファイルパス ][--services= サービスのインスタンス名 ] [--no-start]▼実行例$ grails cf-push --appname=store--url=jggugstore.cloudfoundry.com--services=mysql-99afdca( 省略 )Creating application store at jggugstore.

$ grails cf-stop
Application 'store' stopped.
●cf-restart

アプリケーションを再起動します。

▼使用方法$ grails cf-restart [--appname= アプリケーション名 ]▼実行例$ grails cf-restart

cloudfoundry.com with 512MB
and services [mysql-99afdca]: OK

Application 'store' stopped.

Uploading Application:
Checking for available resources: OK

Trying to start Application: 'store'.

Processing resources: OK.......

Packing application: OK
Uploading (5K): OK

Application 'store' started at http://jggugstore.cloudfoundry.com

Trying to start Application: 'store'.
.....
Application 'store' started at http://jggugstore.cloudfoundry.com■アプリケーションの操作●cf-start

アプリケーションを起動します。

▼使用方法$ grails cf-start [--appname= アプリケーション名 ]Grails Plugin 探訪

第 4 回〜 Cloud Foundry プラグイン〜

37

G*Magazine vol.3●cf-delete-app

アプリケーションを削除します。

▼使用方法$ grails cf-delete-app [--appname= アプリケーション
名 ] [--force]▼実行例$ grails cf-delete-app
Application 'store' uses 'mysql-e244f88' service,would you like to delete it? ([y], n)yApplication 'store' deleted.
Service 'mysql-e244f88' deleted.
●cf-delete-all-apps

すべてのアプリケーションを削除します。

▼使用方法$ grails cf-delete-all-apps [--force]▼実行例■アプリケーションの更新●cf-update

war を作成しアプリケーションを再デプロイします。

▼使用方法$ grails cf-update [--appname= アプリケーション名 ][--warfile=war ファイルパス ]▼実行例$ grails cf-update( 省略 )
Updating Application:
Checking for available resources: OK
Processing resources: OK
Packing application: OK
Uploading (5K): OK
Application 'store' stopped.

Trying to start Application: 'store'.
......

$ grails cf-delete-all-apps --force

Application 'store' started at http://jggugstore.cloudfoundry.com

Application 'store' deleted.

atropos:store rhapsody$

Service 'mysql-eaae347' deleted.
●cf-rename-app

アプリケーション名を変更します。ただし、Config.groovy 中のアプリケーション名は変更しません。
▼使用方法$ grails cf-rename-app 新しい名前 [--appname= アプ
リケーション名 ]▼実行例●cf-update-memory

メモリの割当量を変更します。

▼使用方法$ grails cf-update-memory メモリサイズ [--appname=アプリケーション名 ]▼実行例$ grails cf-update-memory 1G
Updated memory reservation to '1.0G'.

$ grails cf-rename-app jggugstore
Application 'store' stopped.
Renamed 'store' to 'jggugstore'.
If necessary, update @grails.plugin.cloudfoundry.appname@

Trying to start Application: 'store'.

in @Config.groovy@ with the new name.

.........
Application 'store' started at http://jggugstore.cloudfoundry.com
atropos:store rhapsody$

38 Grails Plugin 探訪 第 4 回〜 Cloud Foundry プラグイン〜

G*Magazine vol.3●cf-map

アプリケーションに URL を登録します。1 つのアプリケーションに複数の URL を登録することができます。

▼使用方法$ grails cf-map URL [--appname= アプリケーション名 ]▼実行例$ grails cf-map http://jggugstore2.cloudfoundry.com
Succesfully mapped url$ grails cf-app+-------------+----+--------+-----------------------------------------------------------+---------------+| Application | #| Health | URLs

| Services|+-------------+----+--------+-----------------------------------------------------------+---------------+| store| 1| 0%| jggugstore.cloudfoundry.com, jggugstore2.cloudfoundry.com | mysql-7926b66 |+-------------+----+--------+-----------------------------------------------------------+---------------+●cf-unmap

アプリケーションに登録してある URL を解除します。

▼使用方法$ grails cf-unmap URL [--appname= アプリケーション名 ]▼実行例$ grails cf-unmap jggugstore2.cloudfoundry.com
Succesfully unmapped url$ grails cf-app+-------------+----+--------+-----------------------------+---------------+| Application | #| Health | URLs

| Services|+-------------+----+--------+-----------------------------+---------------+| store| 1| 0%| jggugstore.cloudfoundry.com | mysql-7926b66 |+-------------+----+--------+-----------------------------+---------------+

Grails Plugin 探訪

第 4 回〜 Cloud Foundry プラグイン〜

39

G*Magazine vol.3●cf-update-instances▼使用方法

アプリケーションのインスタンス数を変更します。

$ grails cf-crashlogs [--appname= アプリケーション▼使用方法

名 ] [--instance= インスタンス番号 ]$ grails cf-update-instances インスタンス数▼実行例[--appname= アプリケーション名 ]$ grails cf-crashlogs▼実行例$ grails cf-update-instances 2==== logs/err.log ====

Scaled 'store' up to 2 instances.

# Logfile created on 2011-08-26 23:43:08 +0000 by logger.rb/25413
F, [2011-08-26T23:43:08.609557 #17058] FATAL -- : Memory limitof 256M exceeded.

$ grails cf-app

F, [2011-08-26T23:43:08.609608 #17058] FATAL -- : Actual usage+-------------+----+--------+-----------------------------+---------------+| Application | # | Health | URLs

| Services

was 290M, process terminated.

|+-------------+----+--------+-----------------------------+---------------+| store| 2 | 0%| jggugstore.cloudfoundry.com | mysql-7926b66 |==== logs/stderr.log ====+-------------+----+--------+-----------------------------+---------------+
Aug 26, 2011 11:43:05 PM org.apache.coyote.http11.Http11Protocol
init■アプリケーション情報

INFO: Initializing Coyote HTTP/1.1 on http-53482
Aug 26, 2011 11:43:05 PM org.apache.catalina.startup.Catalina●cf-crashes

アプリケーションのデプロイに失敗した際のクラッシュ情報を表示します。

INFO: Initialization processed in 366 ms( 省略 )▼使用方法$ grails cf-crashes [--appname= アプリケーション名 ]●cf-logs

ログファイルの内容を表示します。

▼実行例

対象は次のとおりです。
• logs/stderr.log$ grails cf-crashes• logs/stdout.log+---------+----------------------------------+--------------------+| Nameload| Instance ID| Crashed Time|+---------+----------------------------------+--------------------+| store-1 | 6afc1a56deaa18f29d90c1850af87b80 | 08/27/2011 08:42午前 |• logs/startup.log▼使用方法$ grails cf-logs [ ファイル名 ] [--appname= アプリケー| store-2 | fad8a76d6068a524c8a9a24ad9f5184f | 08/27/2011 08:42午前 |ション名 ] [--instance= インスタンス番号 ] [--stderr]| store-3 | 73b0f62474c9e9cc5bdb89e2d6b58d3b | 08/27/2011 08:43午前 |[--stdout] [--startup]| store-4 | 3cab1d09832c69b9a79211a055afa49d | 08/27/2011 08:43午前 |+---------+----------------------------------+--------------------+●cf-crashlogs

アプリケーションのデプロイに失敗した際のクラッシュ・ログ

を表示します。

対象は次のとおりです。
• logs/err.log• logs/stderr.log• logs/stdout.log• logs/startup.log

40 Grails Plugin 探訪 第 4 回〜 Cloud Foundry プラグイン〜

G*Magazine vol.3●cf-get-file▼実行例

ファイルをダウンロード、またはファイルの内容を表示します。

$ grails cf-logs

格納先のファイル名を指定すればダウンロード、指定しなければ==== logs/stderr.log ====

ファイル内容の表示となります。

Aug 27, 2011 4:07:07 AM org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-16924
Aug 27, 2011 4:07:07 AM org.apache.catalina.startup.Catalina load

テキスト形式のファイルが対象のようです。

▼使用方法$ grails cf-get-file ファイルパス [ 格納先ファイル名 ]INFO: Initialization processed in 368 ms(省略)
Aug 27, 2011 4:07:13 AM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-16924
Aug 27, 2011 4:07:13 AM org.apache.catalina.startup.Catalina start
INFO: Server startup in 5440 ms[--appname= アプリケーション名 ] [--instance= インス

タンス番号 ]
▼実行例

$ grails cf-get-file logs/stderr.log ./stderr.log
ERROR: There was an error retrieving logs/startup.log, please try
Wrote logs/stderr.log to ./stderr.log

again●cf-list-files$ grails cf-get-file logs/stderr.log▼使用方法

Aug 27, 2011 4:07:07 AM org.apache.coyote.http11.Http11Protocol init

ディレクトリの内容を表示します。

INFO: Initializing Coyote HTTP/1.1 on http-16924$ grails cf-list-files [ ディレクトリパス ][--appname= アプリケーション名 ] [--instance= インス
タンス番号 ]

Aug 27, 2011 4:07:07 AM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 368 ms(省略)
Aug 27, 2011 4:07:13 AM org.apache.coyote.http11.Http11Protocol start▼実行例

INFO: Starting Coyote HTTP/1.1 on http-16924
Aug 27, 2011 4:07:13 AM org.apache.catalina.startup.Catalina start$ grails cf-list-files

INFO: Server startup in 5440 ms

logs/

-

tomcat/-●cf-stats

アプリケーションのリソース使用状況を表示します。

▼使用方法$ grails cf-list-files logs
stderr.log

1.2K

stdout.log

0B$ grails cf-stats [--appname= アプリケーション名 ]▼実行例$ grails cf-stats$ grails cf-list-files tomcatbin/-+----------+-------------+----------------+--------------+---------------+

conf/

-

| Instance | CPU (Cores) | Memory (limit) | Disk (limit) | Uptime

lib/

-

+----------+-------------+----------------+--------------+---------------+

logs/

-

|0

stacktrace.log

0B+----------+-------------+----------------+--------------+---------------+

temp/

-

webapps/

-

work/

-

| 1.3% (4)Grails Plugin 探訪| 309.8M (512M) | 38.0M (2G)|

| 0d:0h:19m:34s |第 4 回〜 Cloud Foundry プラグイン〜

41

G*Magazine vol.3●cf-show-instances●cf-env-add

▼使用方法

▼使用方法

インスタンスの状況を表示します。

アプリケーション用環境変数を追加します。

$ grails cf-show-instances [--appname= アプリケー
ション名 ]▼実行例

リケーション名 ]▼実行例$ grails cf-show-instances$ grails cf-env-add LOCATION Yokohama+-------+---------+--------------------+| Index | State| Start Time

Adding Environment Variable [LOCATION=Yokohama]: OK|+-------+---------+--------------------+| 0$ grails cf-env-add 環境変数名 値 [--appname= アプ| RUNNING | 08/27/2011 01:36 午後 |Application 'store' stopped.

+-------+---------+--------------------+| 1| RUNNING | 08/27/2011 01:59 午後 |+-------+---------+--------------------+...........
Application 'store' started at http://jggugstore.cloudfoundry.com■アプリケーション環境●cf-env-del●cf-env

アプリケーション用環境変数の一覧を表示します。

▼使用方法$ grails cf-env [--appname= アプリケーション名 ]▼実行例

アプリケーション用環境変数を削除します。

▼使用方法$ grails cf-env-del 環境変数名 [--appname= アプリ
ケーション名 ]▼実行例$ grails cf-env$ grails cf-env-del LOCATION+----------+----------+| Variable | Value

Trying to start Application: 'store'.

|

Deleting Environment Variable [LOCATION]: OK+----------+----------+| LOCATION | Yokohama |+----------+----------+

Application 'store' stopped.

Trying to start Application: 'store'.
.
ERROR - Application 'store' failed to start, logs
information below.

42 Grails Plugin 探訪 第 4 回〜 Cloud Foundry プラグイン〜

G*Magazine vol.3■サービス●cf-delete-service

サービスのインスタンスを削除します。

●cf-services

利用可能なサービスの一覧と、作成したサービスのインスタンスを表示します。

$ grails cf-delete-service サービスのインスタンス名

▼使用方法

▼実行例

$ grails cf-services$ grails cf-delete-service mongodb-2c8f174▼実行例

Successfully removed service: mongodb-2c8f174$ grails cf-services============== System Services ==============●cf-bind-service+------------+---------+-------------------------------+| Service▼使用方法| Version | Description|+------------+---------+-------------------------------+| redis| 2.2| Redis key-value store service || mongodb| 1.8| MongoDB NoSQL store|| rabbitmq| 2.4| RabbitMQ messaging service|| postgresql | 9.0| PostgreSQL database service|| mysql| MySQL database service

|

| 5.1

+------------+---------+-------------------------------+

サービスのインスタンスをアプリケーションにバインドしま

す。

▼使用方法
$ grails cf-bind-service サービスのインスタンス名[--appname= アプリケーション名 ]▼実行例$ grails cf-bind-service mongodb-49ecee4
Creating new service binding to 'mongodb-49ecee4' for 'store'.

=========== Provisioned Services ============
Application 'store' updated+---------------+---------+| NameService 'mongodb-49ecee4' added| Service |+---------------+---------+| mysql-99afdca | mysql|

Application 'store' stopped.

+---------------+---------+●cf-create-service

サービスのインスタンスを作成します。

▼使用方法$ grails cf-create-service ベンダー名 ( サービス名 )[ サービスのインスタンス名 ] [--bind]

Trying to start Application: 'store'.
.....
Application 'store' started at http://jggugstore.cloudfoundry.com
atropos:store rhapsody$▼実行例$ grails cf-create-service mongodb
Service 'mongodb-2c8f174' provisioned.

Grails Plugin 探訪

第 4 回〜 Cloud Foundry プラグイン〜

43

G*Magazine vol.3●cf-unbind-service

サービスのインスタンスをアプリケーションからアンバインド

します。

■管理
●cf-user

ログインしているユーザ名を表示します。

▼使用方法

▼使用方法

$ grails cf-unbind-service サービスのインスタンス名[--appname= アプリケーション名 ]$ grails cf-user
▼実行例

▼実行例
$ grails cf-unbind-service mongodb-49ecee4$ grails cf-user[your.email@yourserver.com]

Removing service binding 'mongodb-49ecee4' from 'store'.

■システム

Application 'store' updated●cf-runtimes

Service 'serviceName' removed

サポートしている実行環境の一覧を表示します。

▼使用方法
Application 'store' stopped.

$ grails cf-runtimes▼実行例

Trying to start Application: 'store'.
........

$ grails cf-runtimes

Application 'store' started at http://jggugstore.cloudfoundry.com+--------+-------------+-----------+| Name●cf-clone-services

あるアプリケーションにバインドされているサービスのインス

タンスを別のアプリケーションにクローンします。
▼使用方法$ grails cf-clone-services クローン元アプリケーショ
ン名 クローン先アプリケーション名| Description | Version|+--------+-------------+-----------+| java| Java 6| 1.6|| node| Node.js| 0.4.5|| ruby18 | Ruby 1.8| 1.8.7|| ruby19 | Ruby 1.9| 1.9.2p180 |+--------+-------------+-----------+▼実行例$ grails cf-clone-services store jggugstore
Creating new service binding to 'mongodb-4d4697f' for 'jggugstore'.
Creating new service binding to 'mysql-99afdca' for 'jggugstore'.
Application 'jggugstore' stopped.
Trying to start Application: 'jggugstore'.
.....
Application 'jggugstore' started at http://jggugstore.cloudfoundry.com$ grails cf-apps+-------------+----+--------+-----------------------------+--------------------------------+| Application | #| Health | URLs

| Services|+-------------+----+--------+-----------------------------+--------------------------------+| store| 2| 0%| jggugstore.cloudfoundry.com | mongodb-4d4697f, mysql-99afdca |+-------------+----+--------+-----------------------------+--------------------------------+| jggugstore

| 1

| 0%

| jggugstore.cloudfoundry.com | mongodb-4d4697f, mysql-99afdca |+-------------+----+--------+-----------------------------+--------------------------------+

44 Grails Plugin 探訪 第 4 回〜 Cloud Foundry プラグイン〜

G*Magazine vol.3●cf-frameworks

サポートしているフレームワークの一覧を表示します。

▼使用方法$ grails cf-frameworks▼実行例$ grails cf-frameworks+---------+| Name|+---------+| grails|| lift|| node|| rails3|| sinatra || spring|+---------+

おわりにCloud Foundry プラグインを使えば、Grails アプリケーションのデプロイやサービスとのバインドを、簡単に実行することができます。

制 約 は い ろ い ろ と あ り ま す が、Cloud Foundry で Grails ア プ

リケーションを公開することを考えていらっしゃる場合、是非
Cloud Foundry プラグインを使用することを検討してください。

▼ぐるーびーたん

第 2 話までのあらすじ

プログラマを目指して熊本から上京したぐるーびー

たん。IT 業界の現実にも少しずつ慣れ、先輩社員からお仕事を頼まれるようにもなったのだが。
。
。

※この作品は、たいがいフィクションです。
実在の人物、
団体とは関係ありません。

ぐるーびーたん

第3話

45

G*Magazine vol.3

リリース情報 2011.08.25
GrailsGrails は、Groovy や Hibernate などをベースとしたフルスタックの Web アプリケーションフレームワークです。
URL: http://grails.org/
バージョン : 1.2.5, 1.3.7, 2.0.0.M2■更新情報• 2.0.0.M2 では、エラーレポートと分析表示が強化されたり、付属の DB が H2 に変更されたり、HTML5 スカッフォルドとなる等、1.3 系から大幅に変更されています。
• 2.0.0.M2 リリースノート :http://grails.org/2.0.0.M2+Release+Notes• 山本さんのブログ：
http://d.hatena.ne.jp/mottsnite/20110907/1315412925

GroovyGroovy は、JavaVM 上で動作する動的言語です。
URL: http://groovy.codehaus.org/
バージョン : 1.7.10, 1.8.1, 1.9-beta-1■更新情報• 1.8.1 では、JSON サポートの強化や、take メソッドの追加、いくつかのバグフィックスが行われています。
• 1.8.1 リリースノート : http://jira.codehaus.org/secure/
ReleaseNote.jspa?projectId=10242&version=17223• 1.9-beta-1 では、Project Coin のバイナリリテラルや例外のマルチキャッチの対応、いくつかのバグフィックスが行われています。
• 1.9-beta-1 リリースノート : http://jira.codehaus.org/secure/
ReleaseNote.jspa?projectId=10242&version=17153• 関谷さんのブログ：http://d.hatena.ne.jp/ksky/20110722/p1

Griffon
Griffon は、デスクトップアプリケーションを開発するためのア
プリケーションフレームワークです。
URL: http://griffon.codehaus.org/
バージョン : 0.9.3■更新情報• 0.9.3 では、Groovy 1.8.1 対応や、新しい Archetype が利用可能となったり、デフォルトパッケージ名の付け方が変わった
りと、いろいろと追加・変更がなされています。
• 0.9.3 リリースノート :http://griffon.codehaus.org/Griffon+0.9.3

Gant
Gant は、XML の代わりに Groovy で Ant タスクを記述し実行する
ビルド管理ツールです。
URL: http://gant.codehaus.org/
バージョン : 1.9.6■更新情報• 1.9.6 では、Groovy 1.8.1 および 1.9.0-beta-1 対応が行われています。
• 1.9.6 リリースコメント : http://groovy.329449.n5.nabble.
com/Gant-1-9-6-released-td4623268.html

46 リリース情報

GradleGradle は、Groovy でビルドスクリプトを記述し実行するビルド管理ツールです。
URL: http://www.gradle.org/
バージョン : 0.9.2, 1.0-milestone-3

GaelykGaelyk は、Groovy で記述する Google App Engine for Java 用のライトウェイトなフレームワークです。
URL: http://gaelyk.appspot.com/
バージョン : 1.0■更新情報• 1.0 では、GAE SDK 1.5.2 に対応したり、テンプレートプロジェ
クトを Gradle でビルドできるようになったり、データスト
アに get メソッドが追加されたりと、様々な変更が行われています。
• 1.0 リリースノート : http://gaelyk.appspot.com/download

Google App Engine SDK for Java
Google App Engine SDK for Java は、Java で Google App Engine
用の Web アプリケーションを開発するための SDK です。
URL: http://code.google.com/intl/ja/appengine/バージョン : 1.5.3■更新情報• 1.5.3 では、Blobstore API を使った blob アップロードのサイ
ズ制限がなくなったり、DatastoreService に getIndexes メ
ソッドが追加されたり、Remote API が追加になったりと、様々な変更が行われています。
• 1.5.3 リリースノート : http://code.google.com/p/
googleappengine/wiki/SdkForJavaReleaseNotes

GPars
GPars は、Groovy に直感的で安全な並行処理を提供するシステム
です。
URL: http://gpars.codehaus.org/
バージョン : 0.12GA■更新情報• 0.12GA では、ActiveObject のパフォーマンスが改良された
り、非同期なメソッドを作成するために AST 変換が追加されたり、いくつかバグフィックスが行われています。
• 0.12GA リリースノート : http://jira.codehaus.org/secure/
ReleaseNote.jspa?projectId=12030&version=16994

Groovy++Groovy++ は、Groovy 言語に対して静的な機能を拡張します。
URL: http://code.google.com/p/groovypptest/
バージョン : 0.4.300■更新情報• 0.4.300 では、多くのパフォーマンスの改善が行われている
ようです。

Spock
Spock は、Java や Groovy 用のテストと仕様のためのフレームワー
クです。
URL: http://code.google.com/p/spock/バージョン : 0.5

G*Magazine vol.3

GroovyServ

CodeNarc

GroovyServ は、Groovy 処理系をサーバとして動作させることでgroovy コマンドの起動を見た目上高速化するものです。
URL: http://kobo.github.com/groovyserv/
バージョン : 0.9

CodeNarc は、Groovy 向けの静的コード解析ツールです。
URL: http://codenarc.sourceforge.net/
バージョン : 0.15

■更新情報

• 0.9 では、PWD 環境変数が使えるようになったり、パフォー
マンスの改善、バグフィックスなどが行われています。
• 0.9 チェンジログ : http://kobo.github.com/groovyserv/
changelog.html#ref-changelog

Geb
Geb は、Groovy を使用した Web ブラウザを自動化する仕組みです。
URL: http://www.gebish.org/
バージョン : 0.6.0■更新情報• 0.6 では、TestNG のサポートが追加されたり、Grails 1.3 ベースとなったり、いくつかバグフィックスが行われています。
• 0.6 リリースノート : http://jira.codehaus.org/secure/
ReleaseNote.jspa?projectId=12101&version=16999

Easyb
Easyb は、 ビ ヘ イ ビ ア 駆 動 開 発 (Behavior Driven
Development:BDD) 用のフレームワークです。
URL: http://www.easyb.org/
バージョン : 0.9.8

Gmock
Gmock は、Groovy 用のモック・フレームワークです。
URL: http://code.google.com/p/gmock/バージョン : 0.8.1■更新情報• 0.15 では、新しいルールが 23 追加されたり、いくつかバグ
フィックスが行われています。
• 0.15 リリースノート : http://groovy.329449.n5.nabble.com/
ANN-Announcing-CodeNarc-0-15-td4677503.html

GMetrics
GMetrics は、Groovy ソースコードのサイズや複雑さを計算したり報告するためのツールです。
URL: http://gmetrics.sourceforge.net/
バージョン : 0.3

GContracts
GContracts は、Groovy で契約プログラミングを行うためのフレームワークです。
URL: https://github.com/andresteingress/gcontracts
バージョン : 1.2.4■更新情報• 1.2.4 では、static メソッドがサポートされたり、いくつかバ
グフィックスが行われています。
• 1.2.4 リリースノート : http://blog.andresteingress.
com/2011/06/13/gcontracts-1-2-4-released/

GroovyFX
GroovyFX は、JavaFX を Groovy で書きやすくするためのフレームワークです。
URL: http://groovy.codehaus.org/GroovyFXバージョン : 1.0 AlphaHTTPBuilder
HTTPBuilder は、HTTP ベースのリソースに簡単にアクセスするための方法です。
URL: http://groovy.codehaus.org/modules/http-builder/バージョン : 0.5.1

リリース情報47

■企業スポンサー■個人サポーター
須江 信洋 様関谷 和愛 様
田中

明様

個人サポータ制度のお知らせ
JGGUG では , 2011 年度より個人サポータ制度を始めました .
今まで JGGUG の経済的な基盤はすべて法人会員の年会費に依存していました。 しかし、個人会員も金銭面からサポート

したいという声があがり、JGGUG 運営基盤の裾野を拡げるためにも、個人サポータという形で個人会員からの寄付を受け入れることにした次第です。

個人サポータとなっていただいた方には、一年間にわたって JGGUG が発行する G* Magazine（年数回刊。基本的に電子版として配布予定）に個人サポータとしてお名前を掲載します（掲載を希望しない旨お申し出いただけば掲載しません）。
個人サポータとなるには、まず supporters@jggug.org にメイルで• お名前• 予定金額G* Magazine へのご芳名掲載の可否をお知らせください。追って、運営委員より振込先の情報などを返信します。
皆様のサポートをお待ちしております。

日本 Grails/Groovy ユーザーグループ
代表

山田 正樹

G* Magazine vol.3 2011.09
http://www.jggug.org

発行人：日本 Grails/Groovy ユーザーグループ
編集長：川原正隆
編集：G* Magazine 編集委員（杉浦孝博、奥清隆）
デザイン：㈱ニューキャスト
表紙：川原正隆
編集協力：JGGUG 運営委員会
Mail：info@jggug.org© 2011 JGGUG 掲載記事の再利用についてはCreative Commons ライセンスによります。

Publisher：Japan Grails/Groovy User Group
Editor in Chief：Masataka Kawahara
Editors：G* Magazine Editors Team
(Takahiro Sugiura, Kiyotaka Oku)
Design：NEWCAST inc.
CoverDesign：Masataka Kawahara
Cooperation：JGGUG Steering Committee
Mail：info@jggug.org