ラベル Grails の投稿を表示しています。 すべての投稿を表示
ラベル Grails の投稿を表示しています。 すべての投稿を表示

2013年4月18日木曜日

Cloud FoundryにWebアプリをIntelliJでデプロイする

IntelliJのバージョンいくつかから、Cloud FoundryおよびCloudBeesとの連携機能がバンドルされるようになっていたので試してみました。

ここではとりあえず、GrailsアプリをCloud Foundryにデプロイしてみます。

プロジェクトを作成

プロジェクトがなきゃ始まりませんので……

デプロイするwarファイルを作成する

「Ctrl+Alt+G」から「prod war」コマンドを実行して、デプロイするwarを作成します。

実行設定を作成

「Edit Configuration」から……

「Defaults」から「Cloud Foundry Server」を選択して、緑の+ボタンで実行設定を追加

「Server」タブの「Configure」でCloud Foundryアカウントを設定

「Deployment」タブでデプロイするwarを設定

デプロイするアプリの設定を行う。とりあえず、「Type」を「Grails」に。MongoDBとかサービスを追加するときは「Edit services」から適宜追加。

設定したwarをデプロイ

「Application Servers」タブから、緑の矢印でCloud Foundryに接続、デプロイ

で、warがデプロイされて、ブラウザが開いてアプリケーションに繋がるはず。

アプリケーションを更新したら

「prod war」でもう一回warを作り直して、実行画面から更新。更新中404になるのがイケてない。

うわぁ簡単すぎて書くことない…もうちょっと複雑なことしようとしたらいろいろ出てくるかも、ということで

2012年12月14日金曜日

GrailsでMongoDBの複合キーインデックスに属性を指定するプラグインを公開

先日、Grailsプラグインgrails-mongodb-compound-index-attributesを紹介して、セントラルリポジトリに公開するまでもないので何とかかんとかと書きましたが、公開してもいいですよということなのでやっぱりお言葉に甘えてGrailsセントラルに公開することにしました。

以下、プラグインについて。

MongoDBでは複合キーに対してインデックスをはることができます。 GrailsのMongoDBプラグインでは、以下のように指定します。

class Book {

    static mapping = {
        compoundIndex contributor: 1, name: 1
    }

    String contributor
    String name

}

が、今のところ、このインデックスにユニーク属性などの属性を指定できません。

それを解決するのが、今回公開したMongoDBプラグインのアドオンであるgrails-mongodb-compound-index-attributesです。インストールすると以下のようにできます。

class Book {

    static mapping = {
        compoundIndex contributor: 1, name: 1, indexAttributes: [name: "contributorUniqueName", unique: true]
    }

    String contributor
    String name

}

ブラボーGrailsプラグインシステム

2012年12月11日火曜日

GrailsのプラグインをCloudBeesで公開する

どうも、G* Advent Calendar 2012、11日目担当、@literaliceです。

さて、突然ですが先日、grails-mongodb-compound-index-attributesというGrailsのプラグインを公開しました。

このプラグインは、GrailsのMongoDBプラグインを拡張し、compound indexにunique属性などを追加できるようにするものです。こんな:

class Book {
    static mapping = {
        compoundIndex contributor: 1, name: 1, indexAttributes: [name: "idxName", unique: true]
    }
    String contributor
    String name
}

で、この機能はどうせ将来的にMongoDBプラグインに実装されるでしょうし、わざわざGrails本家のプラグインリポジトリに公開するまでもない気がしますね。

以前はこういうとき、Githubをプラグインリポジトリにして公開していました。

が、ナウい方法はCloudBeesのDEV@cloudを使うことです。

DEV@cloudは登録するとJenkinsサーバーとMavenリポジトリ、Gitのリポジトリをもらえます。

Mavenリポジトリはいろいろハウスキーピングしてもらえますし、何よりプライベートリポジトリにできるのがGithubをムリヤリ使う方法に比べてイケてますね。

と言うわけで前置きが長くなりましたが、GrailsプラグインをCloudBees、DEV@cloudで公開する手順を紹介します。

Grails version
2.1.1

手順

  1. Grailsプラグインを作る
  2. CloudBeesにアカウントを作る
  3. releaseプラグインをCloudBees用に設定する
  4. publish-pluginコマンドでリリース

Grailsプラグインを作る

作ります。例:grails-mongodb-compound-index-attributes

CloudBeesにアカウントを作る

作ります。→CloudBees

releaseプラグインをCloudBees用に設定する

releaseプラグインはGrailsのcreate-pluginコマンドでプラグインを作成すれば入ってるはずです。

このreleaseプラグインを、CloudBees上に作られたWebDAVのMavenリポジトリにリリースするよう設定します。

// BuildConfig.groovy or ~/.grails/settings.groovy
grails.release.scm.enabled = false // プラグインのソースコードはリリースしない

grails.project.repos.default = "releaseRepository" // リリース先未指定時のリリース先
grails.project.repos.releaseRepository.url = "dav:https://repository-[CloudBeesアカウント名].forge.cloudbees.com/release/" // リリース先"releaseRepository"のURL
grails.project.repos.releaseRepository.type = "maven"
grails.project.repos.releaseRepository.username = "[CloudBeesのUser SettingsにあるUsername]"
grails.project.repos.releaseRepository.password = "[CloudBeesのログインパスワード]"

私の場合は、CloudBeesのJenkinsからビルド、リリースしているので、システムプロパティでファイルを渡して、そのファイルから上記設定を読み込むようにしています。その設定ファイルは、CloudBeesに用意されるプライベートなファイルストレージに置いておきます。
例: BuildConfig.groovy

publish-pluginコマンドでリリース

grails publish-plugin --repository=releaseRepository --protocol=webdav

なお、CloudBees上のJenkinsでビルド、リリースする場合は、前述のようにシステムプロパティに認証情報などを記述した設定ファイルのパスを渡し、そのファイルから設定を読み込むようにします。

sh ./grailsw -DhogehogeFile=/private/[CloudBeesアカウント名]/grails/credential.properties publish-plugin --repository=releaseRepository --protocol=webdav --non-interactive

公開したプラグインをGrailsプロジェクトで使用する

このようにプラグインを公開すると、Grailsプロジェクトから以下のようにして使用できるようになります。

//Build.groovy
grails.project.dependency.resolution = {
    // ...

    repositories {
        // ...

        // For the plugin
        mavenRepo "http://repository-[CloudBeesアカウント名].forge.cloudbees.com/release"
    }

    // ...
    plugins {
        // ...

        compile ":[公開したプラグイン名]:[公開したプラグインバージョン]"
    }
}

まとめ

というわけで、こんな感じにCloudBeesを使ってGrailsプラグインを流れるようにpush→Jenkinsビルド→Mavenリポジトリに公開しましょう。

G* Advent Calendar 2012、12日目は@yamkazuさんです。

2012年6月1日金曜日

PaaSでGrailsならCloudBeesがイイ

HerokuとかCloud Foundryとか、いろんなPaaSを試してみましたが、今のところCloudBeesが良い感じだと思いました。

なお、パフォーマンスや安定性を調査したわけではなく、アプリを動かしてみたときの単純な感想です。
Jelasticなど、Javaをメインに想定したPaaSであればGrailsとの相性もいいと思うので、更に調査したいと思っています。

さて、CloudBeesですが、無料範囲だと、1アプリにつき2インスタンス、5アプリまで作れるようです。さらにビルド環境も用意してくれます。
メモリ制限がきついので本格運用するときは有料プランが必要でしょうけど、試しに始めてみるにはいいGrails実行環境なんじゃないでしょうか。

以下いいとこ雑感

Amazon EC2で動作
これはメリットデメリットあると思いますが、とりあえずMongoHQとかMongoLabとかAmazon SESとか、AWS使った豊富なサービスが使えるのが嬉しい。
例えばcloudfoundry.comとかだとそうは行かない
ビルドサーバー付き
DEV@cloudというCloudBeesのサービスでJenkinsサーバーが使えます。
ソースコードをプッシュしたら、CloudBees上のTomcatで公開するまで全自動
HerokuにもBuildpackというシステムがあって、ソースコードをプッシュしたら自動でビルドが走ってデプロイしてくれる…
が、Grailsのように依存性解決なんかでビルドに時間がかかる場合、途中でタイムアウトになってデプロイに失敗するんですよね。2回に1回くらい。
あとやっぱりJavaな人ならJenkinsが慣れてて使いやすいと思いますし
普通にTomcatにデプロイ
Grailsは大体Tomcatで動かしながら開発するのでTomcatが安心
スティッキーセッション、セッションクラスタリングに対応
クラスタリング時のセッションストアはそれなりに価格高いですけど、なんにしてもこの辺はHerokuでは使えません。
再デプロイ時にダウンタイムがない
CloudBeesはアプリケーションアップデート時、新しいクラスタでアプリを起動させてから古いアプリケーションを破棄してくれるのでダウンタイムが発生しません。
URLのGETパラメータが化けない
Cloud Foundryェ…

今のとこの気になる点

独自ドメイン対応
CloudBees上のアプリを独自ドメインで参照する場合、CNAMEでエイリアスとして設定します
普通の人は独自ドメインのルート(example.comとか)にCNAMEレコード作れないと思うので、www.example.com、とかのサブドメインを使わなければなりません。
Herokuだと普通にAレコードで設定できます。
メモリ
無料範囲だと使用できるのが最大256mで、ここからPermGenなんかを割り当てるようです。
Grailsを動かす場合256mのPermanent領域が推奨されるということですが、もちろんそんなに割り当てたら起動できなくなります。
本格的に動かすときは有料プランで

時間があったら、動かしてみた手順でも書いてみます。

2012年1月7日土曜日

Grails2.0のユニットテスト用アノテーションを整理してみる

Grails Version
2.0.0

Grails 2.0から、アノテーションを使ってユニットテストを書くことになってるのですが、
そのアノテーションの使い方が少しややこしかったので整理してみました。あんまり自信ないです。

Grailsのテストで使うアノテーションは以下のとおり。

  • grails.test.mixin.TestMixin
  • grails.test.mixin.Mock
  • grails.test.mixin.TestFor

@TestMixin

使用例
@TestMixin(GrailsUnitTestMixin)

テストクラスに以下のようなミックスインを適用して、テスト用、モック作成用のメソッドやプロパティを追加、設定するのに使用する。

リストを渡せば複数のミックスインを適用できる。

  • grails.test.mixin.support.GrailsUnitTestMixin
  • grails.test.mixin.domain.DomainClassUnitTestMixin
  • grails.test.mixin.services.ServiceUnitTestMixin
  • grails.test.mixin.web.ControllerUnitTestMixin
  • grails.test.mixin.web.FiltersUnitTestMixin
  • grails.test.mixin.web.GroovyPageUnitTestMixin
  • grails.test.mixin.web.UrlMappingsUnitTestMixin
  • grails.test.mixin.webflow/WebFlowUnitTestMixin

以下にいくつかの使用例を挙げる。

GrailsUnitTestsMixin

以下のような機能をテストクラスに追加する、全てのミックスインの基盤となるミックスイン

  • grailsApplicationやmessageSourceなどのGrailsのコンテキストプロパティを追加
  • mockForConstraintsTests、mockForなどの基本的なモック作成メソッドを追加
import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import org.junit.Test

@TestMixin(GrailsUnitTestMixin)
class GenericTests {
    
    @Test
    void genericTest() {
        mockForConstraintsTests(Book)
        def book = new Book(title: "")
        assert !book.validate()
        assert book.errors["title"] == "blank"
    }
}

DomainClassUnitTestMixin

さっきの「GrailsUnitTestsMixin」を適用し、さらにドメインクラスのテスト用にmockDomainメソッドを追加する。

import grails.test.mixin.TestMixin
import grails.test.mixin.domain.DomainClassUnitTestMixin
import org.junit.Test

@TestMixin(DomainClassUnitTestMixin)
class GenericTests {
    
    @Test
    void genericTest() {
        mockDomain(Book)
        def book = new Book(title: "")
        assert !book.save()
        assert book.errors.getFieldError("title").code == "blank"
    }
}

ServiceUnitTestMixin

さっきの「GrailsUnitTestsMixin」を適用し、さらにサービスのテスト用にmockServiceメソッドを追加する。

import grails.test.mixin.TestMixin
import grails.test.mixin.services.ServiceUnitTestMixin
import org.junit.Test

@TestMixin(ServiceUnitTestMixin)
class GenericTests {
    @Test
    void genericTest() {
        mockService(HelloService)
        assert applicationContext.helloService.hello() == "Hello, World"
    }
}

ControllerUnitTestMixin

「GrailsUnitTestsMixin」を適用し、さらにコントローラーのテスト用にmockControllerメソッドやサーブレットコンテキストのモックなどを追加する。

import grails.test.mixin.TestMixin
import grails.test.mixin.web.ControllerUnitTestMixin
import grails.test.mixin.services.ServiceUnitTestMixin
import org.junit.Test

@TestMixin([ControllerUnitTestMixin, ServiceUnitTestMixin])
class GenericTests {
    @Test
    void genericTest() {
        mockService(HelloService)
        
        def controller = mockController(BookController)
        controller.show()

        assert response.text == "Hello, World"
    }
}

などなど。

@Mock

使用例
@Mock(BookController)
  • 指定クラスに応じて、適当にミックスインを適用する
  • 更に指定クラスのモックを作成してコンテキストに登録する

例えば……

  • ドメインクラスの場合: DomainClassUnitTestMixinを適用し、更に指定クラスに対してmockDomainを適用する
  • サービスクラスの場合: ServiceUnitTestMixinを適用し、更に指定クラスに対してmockServiceを適用する
  • コントローラークラスの場合: ControllerUnitTestMixinを適用し、更に指定クラスに対してmockControllerを適用する
  • などなど
import org.junit.Test
import grails.test.mixin.Mock
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes

@Mock([BookController, HelloService])
class GenericTests {
    
    @Test
    void genericTest() {
        def controller = webRequest.currentRequest.getAttribute(GrailsApplicationAttributes.CONTROLLER)
        controller.show()
        assert response.text == "Hello, World"
    }
    
}

@TestFor

使用例
@TestFor(BookController)
  • 指定クラスに対して@Mock相当の機能を適用してモックを作成
  • 指定クラスのモックに、アーティファクト名のプロパティでアクセスできるようにする
  • testで始まるメソッドに、@Testアノテーションを付加

このアノテーションには、複数のクラスを引数に渡すことができない。

import grails.test.mixin.Mock
import grails.test.mixin.TestFor

@TestFor(BookController)
@Mock(HelloService)
class GenericTests {

    void testGenericTest() {
        controller.show()
        assert response.text == "Hello, World"
    }
    
}

ちなみに

Grailsのコマンド、test-appを使ってテストを実行すると、いくつかの処理を自動で行ってくれるようです。

例えば、ドメインクラスがBookで、テストクラスがBookTestsという名前だった場合、BookTestsに@TestFor(Book)が何も書かなくても適用されるように見えます。

この辺の動作はまだよく分かりません。

おわりに

もっぱら使用するのは@TestForと@Mockであって、@TestMixinで直接テストメソッドをミックスインすることはあまりないと思いますが、大体こんな役割になってそう、ということです。

動作確認はしましたが、ソースはあまり追っていないので間違ってるところもあるかもしれません。

気がついたことなどありましたら、教えてもらえると幸いです。

2011年12月6日火曜日

GrailsのプラグインリポジトリとしてGithubを使う

G* Advent Calendar、6日目いってみます。
今日を境に、公開Grailsプラグインの数が爆発的に増える……といいなーという感じで

Grails version
2.0.0.RC1

Grailsプラグインのリポジトリには、Maven形式のリポジトリが使用できます。

つまり、MavenのリモートリポジトリをGithub上に構築する以下の手法を使えば、GraisプラグインのリポジトリをGithub上に持てるわけですね。
Github を Maven 公開リポジトリにする
GitHub を Maven 公開リポジトリにする (Gradle 編)

何ができるの?

例えば、私はGrailsのAWSプラグインにパッチを当てたものをGithub上に公開しています。1

このプラグインは、次のようにしてどこからでも使用できます。

// grails-app/conf/BuildConfig.groovy
// ...
grails.project.dependency.resolution = {
    // ...
    repositories {
        grailsPlugins()
        grailsHome()
        grailsCentral()

        // ...
        // Github上のマイプラグインリポジトリを指定
        mavenRepo "http://literalice.github.com/maven-repo/releases"
    }
}

# 上で指定したプラグインリポジトリからプラグインをインストール
cd grails-project
grails install-plugin aws 1.2.12.1.p1

とまあ、このように、改造したり開発したりした非公式オリジナルプラグインを簡単に公開して使えるようにしちゃおうというのが目的です。

GrailsのプラグインはzipファイルのURLを指定してインストールできるので他にいくらでも方法はあるわけですが、不特定多数に公開して良いなら今のところこれが一番手軽なんじゃないかなぁと。

この記事は、私がAWSプラグインを改造して公開したときの手順を思い出しながら書いていますが、オリジナルのプラグインを公開するときも同じような感じです。

以下がその手順です。

プラグインのソースコードを入手する

  • Github上にあるプラグインのソースコードをフォークして、手元にクローン
  • grails create-plugin コマンドでプラグインプロジェクトを作成する
  • などなど

プラグインのバージョン番号を更新する

既存のプラグインを改造する場合は、バージョン番号がオリジナルのものと被らないように変更しておいた方が良いでしょう。1.0.0.p1、といった風に適当なサフィックスを付けておきます。

    // XXGrailsPlugin.groovy - プラグインディスクリプタ
    // ..
    def version  = "1.0.0.p1"
    // ..

GrailsのReleaseプラグインをインストールする

プラグインのビルド、リポジトリへの公開は、このReleaseプラグインを使います。

Grails2系のcreate-pluginでプラグインプロジェクトを作成した場合は既にプロジェクトにインストールされているかと思いますが、そうでなければ自分でインストールする必要があります。

install-pluginコマンドでインストールせず、必ずBuildConfig.groovy上でプラグインを追加してexport設定を追加します。そうでないと、このマイプラグインをインストールしたときに、インストール先のプロジェクトもreleaseプラグインに推移的に依存するようになってしまいますので。

ちなみにAWSプラグインはinstall-pluginでreleaseプラグインをインストールしていたようなので、改造版ではこちらも修正しています。

// grails-app/conf/BuildConfig.groovy
// ...
grails.project.dependency.resolution = {
    // ...
    plugins {
        // ...
        build(':release:1.0.0.RC3') {
            export = false // このプラグインはインストール先のプロジェクトには不要
        }
    }
}

プラグインをがしかし実装

プラグインをオレオレに改造します。がしかし好きな機能を実装します。

Github上にMavenリポジトリを構築する

好きな機能は実装できましたか? ではプラグインを公開しましょう。

Github を Maven 公開リポジトリにする
を参考に、MavenリポジトリをGithub上に構築します。

おおざっぱに言えば、

  1. ローカルにフォルダを作ります(「D:\mvn」など)。
  2. そのフォルダをgitのリポジトリとして初期化します。
  3. そのリポジトリに「gh-pages」という名前のブランチを作ります。
  4. 作った「gh-pages」ブランチをgithubにプッシュします
  5. おわり

後は、このフォルダにMavenリポジトリ構造でファイルを追加してプッシュすれば、Github上にそのファイルを公開できるわけですね

ここでは、「D:\mvn」にgitのリポジトリを作ったものとします。

ローカルフォルダにGrailsプラグインをリリースする

先ほどgitリポジトリとして初期化したフォルダ「D:\mvn」にプラグインをリリースします。

Grailsのreleaseプラグインは、SubversionリポジトリやリモートのWebDAV Mavenリポジトリにプラグインをリリースできますが、ローカルにはき出すことも可能なようです。

リリース先の指定方法はreleaseプラグインのマニュアルに載っています。

プロジェクトのBuildConfig.groovyか、ユーザーのホームディレクトリにsettings.groovyというファイルを置いて設定することができます。

バージョン管理に入れるであろうBuildConfig.groovyに、ローカルのファイルパスを書き込むというのはダサい気がするので、私はsettings.groovyを使いました。以下のように設定します。

// ~/.grails/settings.groovy
grails.release.scm.enabled = false // プラグインのソースコードはリリースしない

grails.project.repos.default = "localRelease" // リリース先未指定時のリリース先
grails.project.repos.localRelease.url = "file:///D:/mvn" // リリース先"localRelease"のURL

設定が終わったら、プラグインのプロジェクトディレクトリに移動して、リリースコマンドを実行します。2

cd my-grails-plugin
grails publish-plugin

コマンドが正常に終了すれば、「D:\mvn」にプラグインのzipファイル、pomファイルなどが生成されているはずです。3

MavenリポジトリをGithubにプッシュ

「D:\mvn」に生成された全てのファイルをコミットし、github上にプッシュします。

これで、作成したプラグインをどこからでも使用できるようになりました。

公開したプラグインを使う

公開したプラグインは、前述のように「BuildConfig.groovy」にGithub上のプラグインリポジトリを追加すれば使用できます。

リポジトリのURLは、Githubユーザー名が「literalice」、プッシュしたMavenリポジトリのフォルダ名が「mvn」であれば、「http://literalice.github.com/mvn」となります。

// grails-app/conf/BuildConfig.groovy
// ...
grails.project.dependency.resolution = {
    // ...
    repositories {
        // ...
        // Github上のマイプラグインリポジトリを指定
        mavenRepo "http://literalice.github.com/mvn"
    }
}

# 上で指定したプラグインリポジトリからプラグインをインストール
cd grails-project
grails install-plugin myplugin 1.0.0.p1

Twitterとかで通知

使って欲しい人に向けて、プラグインのリリース情報を通知します。プラグインリポジトリのURLとプラグイン名、プラグインバージョンを教えてあげれば大丈夫なはず。

まとめ

これでもう、プラグインのバグ修正も機能追加も待つ必要ないですね。直して公開です。

わざわざGrailsの公式リポジトリにプラグインを公開する必要もないですね。プラグイン名とリポジトリのURLを一緒に教えてあげれば大丈夫です。

気が向きましたら、本家にpull requestとか、プラグインを公式リポジトリに公開とかするとよりナイスですね。

Githubの最大容量は300MBらしいですが、普通のプラグインはそんなに容量大きくならないので大丈夫じゃないでしょうか、多分。

G* Advent Calendar、7日目は@masanobuimaiさんです。



注1: AWSプラグインは、GrailsからAmazon Web Serviceを簡単に使えるようにするものですが、SESでメールを送信するときに文字セットを指定できなかったためパッチを当てて使っています。

注2: 本来であれば、

cd my-grails-plugin
grails publish-plugin --repository=localRelease
とすることで、リリース先を明示的に指定できるはずなのですが、私の環境ですと上手く動かなかったので、デフォルトのリリース先をsettings.groovyで設定して、リリース先を指定せずにリリースできるようにしています。

注3: 「WARNING: unknown protocol 'file' for repository」というワーニングが出るのが気になりますが…とりあえず、問題なくリリースできるようです。

2011年11月28日月曜日

Load the JQuery Library via CDN with Grails Resources Plugin

Grails version
2.0.0.RC1
Resources Plugin version
1.1.1

You can use the jquery library on existing CDN networks with Grails Resources Plugin in this way.

// grails-app/conf/XXResources.groovy
modules = {
    // Defines JQuery's CDN network
    overrides {
        def jqueryVersion = org.codehaus.groovy.grails.plugins.jquery.JQueryConfig.SHIPPED_VERSION
        'jquery' {
            resource id:'js',
                     linkOverride:"http://ajax.googleapis.com/ajax/libs/jquery/${jqueryVersion}/jquery.min.js"
        }
    }
}

Note: The resources plugin load a resource via 'linkOverride' url only when it is not in debugging mode.
see also: Grails Resources Plugin: Debbugging

2011年7月14日木曜日

JQueryUI Theme in Grails Resources Plugin

Grails version : 1.3.7
Resources Plugin version : 1.0

Resources PluginとJQuery UI Pluginと使っているときに、JQuery UIのカスタムThemeを指定する方法が分からなかったのですが、
以下のようにすることで今のところ正常に動いています。

conf/XXResources.groovy (Resources configuration file)
modules = {
  overrides {
    'jquery-theme' {
      // location of a jquery theme css
      resource id:'theme', url:'/jquery-ui/themes/cupertino/jquery-ui-1.8.11.custom.css'
    }
  }
}

2011年4月6日水曜日

GrailsのMailプラグインでエンコード指定(ISO-2022-JP)なメールを送る

Grailsのバージョンは1.3.7
Mailプラグインのバージョンは1.0-SNAPSHOT

結構やってるんだけど、やり方忘れるので備忘録ということで

import org.springframework.mail.javamail.MimeMailMessage
import org.springframework.mail.javamail.MimeMessageHelper

def mailService

/**
* メールを送信する
*
* @param mailParams メールサービスへ渡すパラメータ
*/
def sendMail(Map mailParams) {
mailService.sendMail {
helper = new MimeMessageHelper(
mailSender.createMimeMessage(), multipart, "ISO-2022-JP")
message = new MimeMailMessage(helper)

to mailParams.to
from mailParams.from
subject mailParams.subject
body mailParams.body
}
}

上のコードをサービスクラスとかに入れて呼び出します。
Mailプラグインの設定方法はここに書いてあるままです。

Grails Mail Plugin

最近はUTF-8でもいいんでしたっけ?
でもまあ日本語メールならISO-2022-JPにしといて損はないんじゃないでしょうか。

2010年7月30日金曜日

Grailsを1.3.3に上げたら、ユニットテストがNullPointerExceptionで壊滅

Grailsのバージョンはもちろん1.3.3

タイトル通り、Grailsを1.3.3に上げたら、ユニットテストがNullPointerExceptionで壊滅しました
原因はGRAILS-6482で1.3.4で直るらしいですが、とりあえず回避方法としては次のコードをユニットテストにいれるといいらしいです。
protected void setUp() {
super.setUp()
PluginManagerHolder.pluginManager = [hasGrailsPlugin: { String name -> true }] as GrailsPluginManager
}

protected void tearDown() {
super.tearDown()
PluginManagerHolder.pluginManager = null
}

ユニットテストが多くなってくるとめんどいので、_Events.groovyに次のコードを入れました。
import org.codehaus.groovy.grails.plugins.GrailsPluginManager
import org.codehaus.groovy.grails.plugins.PluginManagerHolder

eventTestPhaseStart = { phase ->
if (phase == 'unit') {
PluginManagerHolder.pluginManager = [hasGrailsPlugin: { String name -> true }] as GrailsPluginManager
}
}

eventTestPhaseEnd = { phase ->
if (phase == 'unit') {
PluginManagerHolder.pluginManager = null
}
}

これでいいかどうかは不明ですがとりあえずNullポは消えたので。

2010年7月9日金曜日

hudson+antでgrailsアプリをビルドする その1

Grailsアプリケーションをhudson上でビルドするにはいくつか方法があります。
hudsonにGrailsプラグインがあるので、それを使用してビルドすることもできます。シェルでgrailsコマンドを呼び出すこともできます。
しかし、後々ビルド手順が複雑になったりしたときなどを考えると、やはりantのbuild.xmlにビルド手順をまとめて、hudson上からはbuild.xmlのデフォルトターゲットを呼び出すだけ、くらいにしておいたほうがいいかと。
いや、gantでもpomでもいいんですけどね。

まず、build.xmlを作成するところまで書いてみます。ポイントは、

では↓

1:build.xmlを生成
というか、grailsが作ってくれます。
cd [grails-project]
grails integrate-with --ant
これで、Grailsプロジェクトの中にbuild.xml、ivy.xml、ivysettings.xmlができているはず。
(ちなみにこれらのファイルはgrails upgradeでなぜか更新されないので、grailsを更新したらこれらのファイルを削除してもういちどintegrate-withコマンドを実行)

antコマンドを試してみましょう。基本的にgrailsコマンドに対応したantターゲットができています。
必要な環境変数を設定して、実行
普通にやるとメモリ不足で落ちることが多いので、ヒープサイズも指定しておきます。
set GRAILS_HOME=C:\grails\grails-1.3.2
set ANT_OPTS=-Xmx1024m -XX:MaxPermSize=512m

ant compile

download-ivy:
...(略)...

[ivy:retrieve] ::::::::::::::::::::::::::::::::::::::::::::::
[ivy:retrieve] :: UNRESOLVED DEPENDENCIES ::
[ivy:retrieve] ::::::::::::::::::::::::::::::::::::::::::::::
[ivy:retrieve] :: org.jboss.netty#netty;3.1.5.GA: not found
[ivy:retrieve] ::::::::::::::::::::::::::::::::::::::::::::::
[ivy:retrieve]
[ivy:retrieve]
[ivy:retrieve] :: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS

BUILD FAILED
D:\project\literalice\sandbox\grails-ant-build\build.xml:60: impossible to resolve dependencies:
resolve failed - see output for details
…失敗。あれ?
これですね。
http://jira.codehaus.org/browse/GRAILS-6410
1.3.4で直るらしい。でもまあたぶん、リポジトリ追加してやればいいんじゃないかな…

2:依存先リポジトリの追加
先ほど生成したivysettings.xmlに、antビルドが使用しているリポジトリがリストされているので、ここにさっき足りない言われたライブラリのリポジトリを追加します。

















で、もういちど
ant compile
...(略)...
compile:
[grailsTask] Resolving dependencies...
[grailsTask] Dependencies resolved in 738ms.

BUILD FAILED
D:\project\literalice\sandbox\grails-ant-build\build.xml:104: The following error occurred while executing this line:
D:\project\literalice\sandbox\grails-ant-build\build.xml:48: Unable to start Grails: java.lang.reflect.InvocationTargetException
うは失敗しかもイミフ

結論から言うと、antのバージョンが1.8系だとダメみたい。
antのバージョンを1.7.1にしてもう一回!
ant compile
...(略)...
[grailsTask] [groovyc] Compiling 7 source files to D:\project\literalice\sandbox\grails-ant-build\target\classes
[grailsTask]

BUILD SUCCESSFUL
Total time: 5 seconds
OK。

3:依存先リポジトリの追加
さて、私としては、ビルド手順は極力build.xmlに集めたいです。
なので、test、warなどのコマンドをターゲットを順に呼び出すターゲットを作成します。
私はbuild.xmlに直接追加しましたが、別のビルドファイルを作成して、そのファイルからbuild.xmlを呼び出すようにしてもいいかもしれません。

これでもいいですが、私の場合war作成時のビルド環境(dev,prod,test,etc...)を明示したかったので、以下のように修正しました。
あと、non-interactiveをコマンド引数に指定しないと、hudsonに持って行ったときにループっぽくなってビルドが進まなくなります。

まず、antのgrailsマクロにビルド環境の引数を追加します。













つぎにデフォルトターゲットに指定するビルド用ターゲット







これでbuild.xmlができました。あとはhudson上に持って行ってビルドするだけです。
ただし、このままだとgrailsがビルドやテストに使用するディレクトリ(普通は%HOME%/.grails以下)をhudsonジョブ間で共有します。
並行ビルドなど考えると、各ジョブ間の作業ディレクトリはなるべく分割しておきたいところです。

このへんは次回。

2010年7月6日火曜日

GrailsアプリケーションのAntビルドでSSLHandshakeException

grails1.2系で、初めてプロジェクトをビルドするときにantでビルドすると、こんなエラーが起こることがあります。
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException

これが発生するのは、プロジェクトはあるけど、まだ作業ディレクトリ(.grails)がない状態。つまり、
  • Grailsプロジェクトを新マシンにチェックアウトしてantでビルド
  • 作業ディレクトリを~/.grailsから別の場所に変更してantでビルド
などのときに発生するようです。

Tomcatのプラグインを、httpsのプラグインサイト上から落そうとして証明書エラー…javaが知らない認証局を使ってるらしい。
プラグインサイトが使用している中間認証局の証明書をkeytoolでインポートすれば、とりあえずビルドはできます。

証明書: https://www.startssl.com/certs/sub.class2.server.ca.crt

keytool -import -keystore %JAVA_HOME%/jre/lib/security/cacerts -file sub.class2.server.ca.crt -alias sub.class2.start.com
パスワードは変更していなければ「changeit」
証明書インポートについては、HttpClientでSSL通信が詳しいです。

なお、Grails1.3以降だとtomcatプラグインはネットワークからではなくGRAILS_HOMEから持ってくるようで、このエラーは発生しませんでした。

2010年2月16日火曜日

IntegrationTestのときにタイムスタンプの自動設定を切りたい

Grailsのバージョンは1.2.1

lastUpdatedでソートしてるときとか、タイムスタンプの自動設定を切りたいんです。

[DomainClass]が何かのドメインだとして、
DomainClass.mapping.autoTimestamp(false)

まあGroovy的には当然かもですが。

2010年1月10日日曜日

Grailsの統合テスト内で、タグライブラリを呼び出す

Grailsのバージョンは1.2.0

備忘録的なメモ

タグライブラリやコントローラーのIntegration-Test書いてると、テストの中で標準(g)のタグライブラリ呼び出したくなることありませんか?あんまないか。


import org.codehaus.groovy.grails.commons.ApplicationHolder
import org.codehaus.groovy.grails.web.taglib.NamespacedTagDispatcher

class CustomTagLibSpecification extends GroovyPagesSpecification {
  NamespacedTagDispatcher g = ApplicationHolder.application.mainContext.getBean('gspTagLibraryLookup').lookupNamespaceDispatcher('g')

 def "仕様かきかき"() {
  message = g.message(code:"message.properties.message.code")
  ...
 }
}


上のはSpockの仕様ですけど、普通のGroovyPagesTestCase使ったテストケースでもいけるんじゃないかと。まあ、applyTemplate使ってもいいすけど。
コントローラ内だったら、なんにもしなくても message(code:"ほげほげ") でタグライブラリ呼び出せるんだけど…

もっといい方法ありましたら教えてくださいです。

2009年12月23日水曜日

Grailsのtest-appで、コンソールにstacktraceを出す

Grailsのバージョンは1.2.0.RC2

Grailsには、組み込みのテスト実行環境がついています。
Grailsをインストールして、Grailsのプロジェクトを作成して、
コンソールで"test-app"と打ち込むだけでテストが実行されます。

んですが、このテスト、実行してエラーがでても、コンソールにスタックトレースがでないんですよね。

testSample...FAILED

つー、そっけない一行がでるだけ。なんでやねん。

というわけで、Grailsのテストイベントを取ってスタックトレースを出してみます。

1.スタックトレースを出力するためのイベントリスナを作成する。
以下のようなスタックトレース出力クラスを作ります
package testsupport
import grails.build.GrailsBuildListener;
public class StackTraceDumper implements GrailsBuildListener {
void receiveGrailsBuildEvent(String name, Object[] args) {
if (name == 'TestFailure') {
this.doTestFailure(*args)
}
}
protected doTestFailure(String name, failure, boolean isError) {
failure?.printStackTrace()
}
}

2.上記イベントリスナをコンパイル
なんか、イベントリスナだけは前もってコンパイルしとかないと失敗しました。
[grails-project]/src/groovyの下に入れて"grails compile"を実行してもいいし、
jarにして[grails-project]/libにいれてもいいと思います。

3._Events.groovyスクリプトの記述
上記イベントリスナを設定します。
[grails-project]/scripts/_Events.groovyに以下を記述
import testsupport.StackTraceDumper
eventListener.addGrailsBuildListener(new StackTraceDumper())

4.test-appコマンドを、-echoErrオプション付きで実行する
Grails1.2.0から、echoErrオプションで標準エラーを出力できるようになりました。
"test-app -echoErr"と激しく打ち込み。
テストクラスを限定したり、ユニットテストだけ実行したりできます
"test-app [TEST CLASS] -echoErr"
"test-app unit: -echoErr"

で、スタックトレースがでるはず…


ちなみに、spockプラグインを使うと、powerassertsっぽいかっこいいスタックトレースになります。
こんな↓


あ、[grails-project]/scripts/_Events.groovyの仕様とかよく分かってないんで、突っ込みどころあったら突っ込んでくらはい

2009年12月8日火曜日

[Grails] GSP部分テンプレート内のitがときどき消滅する件

grailsのバージョンは1.2-M4

grailsで部分テンプレート「/share/_book.gsp」を表示するとき、



とかやると思います。
このとき、部分テンプレート「/share/_book.gsp」内の変数「it」にbookが自動で設定されるわけですが、
この「it」にアクセスできないことがあるようです。

具体的には、部分テンプレート「/share/_book.gsp」内で

${it.title.encodeAsHTML()}

というふうに、linkタグを使っているときにlinkタグテキスト部内のit(it.title.encodeAsHTML())にアクセスできないみたいです。この場合、次のようにdelegateからアクセスする必要があります。

${delegate.it.title.encodeAsHTML()}


テキスト部は、クロージャになってタグライブラリの引数に渡されるので、delegateの差し替えなどしていない場合上のようになる…のかな?

2009年11月6日金曜日

Grails + easyb でイミフなエラー(MultipleCompilationErrorsException)が出る件

Grailsのバージョンは1.2-M3
EasyB + Grails Testingプラグイン(grails-easyb)のバージョンは1.1

ちょっと前、GrailsでRSpec的なBDDができたらいいなぁ、的なこと言ってたんですが、どうやら"easyb" というBDDフレームワークがあって、Grailsでも使用できる模様。([Java][Groovy][BDD] Grails におけるドメインクラスのユニットテスト - Grails標準テストと Easyb プラグイン使用)

人によるかもですが、私の場合xUnit系のテストケースよりはBDDのスペックの方が断然書きやすいので、早速飛びついてみました。
easyb公式ページなどを参考に、簡単なシナリオを書いてみる…

で、タイトル通り怒られました。このメッセージがよく分からない…
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed, Script1.groovy: 7: unexpected token: } @ line 7, column 5.
グーグル先生とかに相談してみたんですがさっぱり。
適当に試行錯誤してみました。まずコレ。
scenario "TestScenario", {
given "TestGiven", {
}
when "TestWhen", {
}
then "TestThen", {
"test".shouldBe("test")
}
}
このコードなら通してくれました。
だから何?って感じの超テストコードなんですが、ここから段々デコレーションしてみます。

といいますか、どうやら日本語が含まれてるとだめっぽい。コメントすら無理
でもシナリオ名に日本語使ったとき、たまに通ることもある…

あーもうだめだ、再現性ないとかお試しで頑張るには手強すぎる。
シナリオの書き方が違ってるのか?コメントはどこどこに入れてはいけないとか…そんなバ
サービスのインジェクションとかどうするのかなー、とか他にもいろいろ調べたかったんですけど、とりあえず様子見ということで。

しばらく標準のテストで進めるか…

2009年11月3日火曜日

[Grails] Integration Test のテストケースは、GrailsUnitTestCaseでなくGroovyTestCase を継承するらしい

Grailsのバージョンは1.2-M3

情報元はここ
http://agileice.blogspot.com/2009/09/grails-integration-testing.html

なんか、GrailsのIntegrationTestで、Config.groovyに設定した定義情報が使えないなあ、って思ってネットサーフィンしてたら、上記のページを発見。

テストケースの継承元を間違うと、テストケースのtearDownでConfig.groovyの設定情報がnullになってしまうらしいです。
  • GrailsUnitTestCaseとか、それ継承してるControllerUnitTestCaseとか
    Unitテストのみに使用
  • GroovyTestCase
    Integration Test で使用
ポイントは、Integration Testのテストケース全部にGroovyTestCase を使うことですかね。
一個でもGrailsUnitTestCaseのが混じってると、その後のテストケースで設定情報が消えます。

最近のGrailsだと、コントローラーやサービス作成時にUnit Testを生成するから、そのファイルをIntegration Testの方に移動したりしているうちに段々ごっちゃになってそう。

2009年10月29日木曜日

Grails+Acegi プログラム上でログイン・ログアウトさせる

Grailsのバージョンは1.2-M3
Acegi Security は 0.5.2

忘れないうちにメモ

import org.springframework.security.providers.UsernamePasswordAuthenticationToken as AuthToken
import org.springframework.security.context.SecurityContextHolder as SCH

class NantokaService {
def daoAuthenticationProvider

// ログイン処理
void systemLogin(String username, String password) {
def auth = new AuthToken(username, password)
def authtoken = daoAuthenticationProvider.authenticate(auth)
SCH.context.authentication = authtoken
}
// ログアウト処理
void systemLogout() {
SCH.context.authentication = null
}
}

こんな感じ

2009年10月27日火曜日

GrailsのUrlMappingsから直接viewに飛ばすと、localeの読み込みがおかしくなる

Grailsのバージョンは1.2-M3

ここで四時間くらい詰まった…

開発中アプリのトップページ「localhost/sampleapp/」に、"messages_ja.properties"から読み込んだメッセージを表示させていたんです。
で、URLの末尾に「?lang=de」とかつけるとそのメッセージがドイツ語(messages_de.properties)に変わるはずじゃないですか。

でも、変わらなかったんですよね。ブラウザの設定から日本語を消しても、クッキーを消しても、ずっと日本語表示のまま。

こういうときはGoogleいったあと、JIRAでしょうか。
http://jira.codehaus.org/browse/GRAILS-3871
に原因が書いてました。

Grailsのデフォルトだと、UrlMappingsに
"/"(view:"/index")

って書いてて、トップーページをviewに直接遷移させてるんですけど、
この場合、そのviewでlocaleの変更ができなくなるみたい。

いちど
"/"(controller:"foo")
でコントローラーを呼び出して、その中でindex.gspをrenderするように修正することで直りました。


だいぶ有名な事象みたいだけど、これ、直るのかな…
JIRAみると、必殺技「仕様です」みたいにも読めるけど。