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月16日金曜日

ConfluenceのHTML→PDF変換ライブラリを利用する

JIRA Advent Calendar 2011、15日目です。

ってすみません、ConfluenceネタですがまあJIRA使ってる人は連携して使ったりしますよねとか、そんな感じで。
GradleのユーザーガイドPDFに変換するときにお世話になったので、紹介してみます。

Confluenceは、バンドルされているプラグインでページをPDFに変換して出力できるのですが、この機能に使われているライブラリを使って、XHTMLをPDFに変換しようという話です。

ConfluenceのXHTML→PDF処理にはFlying SaucerというJavaのライブラリが使われています。
Flying Saucerを使うと、XMLやXHTMLファイルを、PDFやSWT、Swingのスクリーンに出力させることができます。デザインはCSSで指定できます。

ただ、このFlying Saucer、PDFに変換したときに、文字列が空白位置でしか改行されません。
英文Onlyならいいのですが、日本語で長文をだらだら書いていると画面外にあふれて見えなくなってしまうわけですね。
ConflueceのPDFエクスポートでの禁則 - azuki note

この問題を、Atlassianさんがパッチを当てて修正しているので、しかもMavenリポジトリ上に上げてくれているので、今回はそれを使います。

@Grape+Groovyスクリプトでもいいですが、いろいろ面倒なのでGradleスクリプトで。

フルのサンプルはGithubに上げたのでそちらを見てもらうとして、ポイントだけ書いておきます。

変換元のHTMLとCSSのサンプル

AtlassianさんのMavenリポジトリからFlying Saucerを取得

AtlassianさんのMavenリポジトリから、パッチの当たったFlying Saucerライブラリを取得します。
GradleとかMavenとかIvyとか、そのあたりの設定に依存関係を追加するのがいいですね。

ライセンスが気になるところですが、LGPLのままみたいなので大丈夫でしょう、多分…

Mavenリポジトリ
https://maven.atlassian.com/repository/public
アーティファクト
org.xhtmlrenderer:xhtmlrenderer:8.3-atlassian

Flying SaucerはPDF生成にiTextを使うのでそちらも

Mavenリポジトリ
セントラルリポジトリ
アーティファクト
com.lowagie:itext:2.0.8
// Gradleの例
buildscript {
    repositories {
        mavenCentral()
        mavenRepo url: "https://maven.atlassian.com/repository/public"
    }
    dependencies {
        classpath "org.xhtmlrenderer:xhtmlrenderer:8.3-atlassian"
        classpath "com.lowagie:itext:2.0.8"
    }
}

PDFへの変換処理

PDFへの変換処理は、次のような感じで書きます。

import org.xhtmlrenderer.pdf.ITextRenderer

def renderer = new ITextRenderer()
renderer.setDocument(new File("sample.html"))
renderer.layout()
new File("sample.pdf").withOutputStream {
    renderer.createPDF(it)
}

このコードを実行すれば、sample.htmlがsample.pdfに変換されます。

CSSでPDFに埋め込むフォントを指定する

日本語を使うには日本語フォントをPDFに埋め込むか参照させる必要があります。変換元のHTMLに次のようなCSSを適用しましょう。

@font-face{
    font-family: "IPAexGothic";
    src: url(ipaexg.ttf);
    -fs-pdf-font-embed: embed;
    -fs-pdf-font-encoding: Identity-H;
}

body {
    font-family: "IPAexGothic";
}

なんか、使えるフォントと使えないフォントがあるみたいです。

fsなんたらはFlying Saucerのベンダー拡張ですね。詳しくはFlying Saucerのユーザーガイドを見てみてください。

長い文章を折り返すための設定

Atlassianさんのパッチが当たったFlying Saucerを使うと、次のようにCSSでword-wrapを指定することで長い文章が折り返されるようになります。

p {
    word-wrap: break-word;  
}

おわりに

いやぁ、便利ですね。ちょっとしたPDF作るだけなら、それ用のHTMLファイル作って変換するのが簡単かもしれません。

さて、かなり脱線気味でしたが、こんなところで。
引き続きJIRA Advent Calendar 2011をお楽しみください。