GRAILS導入メモ(1)

ソフトウェアバージョン

導入手順

Java SDK
# rootユーザで実施

####
#### RPM版をインストール
####
# JavaSDK: /usr/java/jdk1.6.0_27 にインストールされる
# JavaDB(Derby): /opt/sun/javadb にインストールされる
./jdk-6u27-linux-x64-rpm.bin

# 解凍されてできたRPMを削除
rm jdk-*.rpm sun-javadb-*.rpm

####
#### 環境変数 JAVA_HOME 設定
####
echo "export JAVA_HOME=/usr/java/default" > /etc/profile.d/java.sh
GRAILS
# rootユーザで実施 (一般ユーザの方がいいのか?)

####
#### インストール
####
# ディレクトリ作成
mkdir /opt/grails
cd /opt/grails
# 解凍
unzip /root/grails-1.3.7.zip
# リンク作成
ln -s grails-1.3.7 default

####
#### 環境変数 GRAILS_HOME 設定
####
echo "export GRAILS_HOME=/opt/grails/default"  >> /etc/profile.d/grails.sh
echo 'pathmunge $GRAILS_HOME/bin' >> /etc/profile.d/grails.sh 

####
#### 動作確認
####
# ログインしなおして環境変数を反映してから・・・
grails

# 下記が出力されればよい
#    Welcome to Grails 1.3.7 - http://grails.org/
#    Licensed under Apache Standard License 2.0
#    Grails home is set to: /opt/grails/default
#
#    No script name specified. Use 'grails help' for more info or 'grails interactive' to enter interactive mode

クイックスタートを実施してみる

GRAILSアプリの生成
# GRAILSのアプリを入れるディレクトリを作成
mkdir /grails

# GRAILSのアプリ生成
cd /grails
grails create-app my-project

# 下記が出力されればよい
#    Welcome to Grails 1.3.7 - http://grails.org/
#    Licensed under Apache Standard License 2.0
#    Grails home is set to: /opt/grails/default
#
#    Base Directory: /grails
#    Resolving dependencies...
#    Downloading: /opt/grails/default/lib/gpars-0.9.jar ...
#    Download complete.
#
#    ・・・(中略)・・・
#
#    Installed plugin tomcat-1.3.7 to location /root/.grails/1.3.7/projects/my-project/plugins/tomcat-1.3.7. ...
#    Executing tomcat-1.3.7 plugin post-install script ...
#    Plugin tomcat-1.3.7 installed
#    Plugin provides the following new scripts:
#    ------------------------------------------
#    grails tomcat
#    Created Grails Application at /grails/my-project
生成後のディレクトリ構成

こんなことになっていた。

/grails
└── my-project
    ├── grails-app
    │   ├── conf
    │   │   ├── hibernate
    │   │   └── spring
    │   ├── controllers
    │   ├── domain
    │   ├── i18n
    │   ├── services
    │   ├── taglib
    │   ├── utils
    │   └── views
    │       └── layouts
    ├── lib
    ├── scripts
    ├── src
    │   ├── groovy
    │   └── java
    ├── test
    │   ├── integration
    │   └── unit
    └── web-app
        ├── css
        ├── images
        │   └── skin
        ├── js
        │   └── prototype
        ├── META-INF
        └── WEB-INF
            └── tld
Domain Class作成
# 作成
cd /grails/my-project
grails create-domain-class org.example.Book

# 下記が出力されればよい
#    Welcome to Grails 1.3.7 - http://grails.org/
#    Licensed under Apache Standard License 2.0
#    Grails home is set to: /opt/grails/default
#
#    Base Directory: /grails/my-project
#    Resolving dependencies...
#    Dependencies resolved in 3939ms.
#    Running script /opt/grails/default/scripts/CreateDomainClass.groovy
#    Environment set to development
#        [mkdir] Created dir: /grails/my-project/grails-app/domain/org/example
#    Created DomainClass for Book
#        [mkdir] Created dir: /grails/my-project/test/unit/org/example
#    Created Tests for Book

# 作成されたファイルを除いてみる
cat /grails/my-project/grails-app/domain/org/example/Book.groovy 

#    package org.example
#
#    class Book {
#
#        static constraints = {
#        }
#    }
Domain Class編集
package org.example

class Book {
    String title
    String author

    // validation 的なものらしい (not null 制約的な)
    static constraints = {
        title(blank: false)
        author(blank: false)
    }
}
Controller 作成
# 作成
cd /grails/my-project
grails create-controller org.example.Book

# 下記が出力されればよい
#    Welcome to Grails 1.3.7 - http://grails.org/
#    Licensed under Apache Standard License 2.0
#    Grails home is set to: /opt/grails/default
#
#    Base Directory: /grails/my-project
#    Resolving dependencies...
#    Dependencies resolved in 2408ms.
#    Running script /opt/grails/default/scripts/CreateController.groovy
#    Environment set to development
#        [mkdir] Created dir: /grails/my-project/grails-app/controllers/org/example
#    Created Controller for Book
#        [mkdir] Created dir: /grails/my-project/grails-app/views/book
#    Created Tests for Book

# 作成されたファイルを除いてみる
# 名前が *Controller であることは重要らしい(それでコントローラを見つけるとかなんとか・・・これがCoCの一端か)
cat /grails/my-project/grails-app/controllers/org/example/BookController.groovy 
#    package org.example
#
#    class BookController {
#
#        def index = { }
#    }
Controller 編集

scaffoldでCRUD処理を簡単にできるコードを生成してもらえるということか・・・?

package org.example

class BookController {
    def scaffold = Book
}
起動

あれ、コントローラに対して常にVIEWを作らなければいけないんじゃ・・・。それをscaffoldで生成してくれるんだろうか。まぁとりあえず書かれている通りに起動しよう。GRAILSに同梱されているTomcatが起動されるらしいから。

cd /grails/my-project/
grails run-app

# 下記が出力されればよい
#    Welcome to Grails 1.3.7 - http://grails.org/
#    Licensed under Apache Standard License 2.0
#    Grails home is set to: /opt/grails/default
#
#    Base Directory: /grails/my-project
#    Resolving dependencies...
#    Dependencies resolved in 3466ms.
#    Running script /opt/grails/default/scripts/RunApp.groovy
#
#    ・・・(中略)・・・
#
#    Running Grails application..
#    Server running. Browse to http://localhost:8080/my-project

# ホスト名に対応するエントリを hosts に入れていなかったら、下記のエラーが出た。
# エントリを追加したら解消。
#    2011-08-28 11:33:11,879 [main] ERROR ehcache.Cache  - Unable to set localhost. 
#                                   This prevents creation of a GUID. Cause was: lvmel6003: lvmel6003
#    java.net.UnknownHostException: lvmel6003: lvmel6003
#    	at java.net.InetAddress.getLocalHost(InetAddress.java:1360)
#    ・・・長大なスタックトレース(略)・・・
ブラウザからアクセス

http://x.x.x.x:8080/my-project

上画面の org.example.BookController をクリックすると
http://x.x.x.x:8080/my-project/book/list

New Book で本を登録してみると


再起動すると登録した本は消えていた

永続化のためのDB設定とかは一切やってないからなぁ。というか起動中はどこに保存していたんだ・・・メモリ?

What's Next を実施してみる

先ほどの永続化関連の疑問にもここである程度答えがわかるようだ・・・

BootStrapにアプリケーション起動時にしたいことを追加する

追加先は /grails/my-project/grails-app/conf/BootStrap.groovy で、初期状態では何も処理していない。
init がアプリケーション起動時に実行するクロージャ、destroy が停止時に実行するクロージャ
このぐらいなら普通にメソッドにして呼んでも良さそうだが、あえてクロージャになってるのは何故なんだろう。

class BootStrap {

    def init = { servletContext ->
    }
    def destroy = {
    }
}

Book テーブルにレコードが存在しない場合のみ、レコードを追加する処理を入れる。

import org.example.Book

class BootStrap {
    def init = { servletContext ->
        // Bookテーブル内のレコード数はこれでとれてしまう
        if (!Book.count()) {
            // failOnError が指定されていると永続化の保存に失敗した場合に Exception を投げる
            // デフォルトだと save() は失敗時に null を返して Exception は投げない
            new Book(author: "Stephen King", title: "The Shining").save(failOnError: true)
            new Book(author: "James Patterson", title: "Along Came a Spider").save(failOnError: true)
        }
    }

    def destroy = {
    }
}

起動し直してみると、最初からたしかに2レコードが表示される。
起動にかかる時間は30秒程度。結構起動は時間がかかる印象だが、実際使う場合はアプリケーションのホットデプロイとかでできるんでしょう?ということで特に問題ではないと思っている。

DataSource を変更して、揮発しないDBにしてみる(初期状態確認)

DataSource の設定は /grails/my-project/grails-app/conf/DataSource.groovy にある。

  • 初期状態は HSQLDB のin-memoryデータベースに接続するようになっている。
  • grails run-app 実行時に 『Environment set to development』が出力されるので、environments の development の設定内容が反映されていると思われる。開発・試験・本番で設定が選べそうなのは嬉しい。どこでスイッチできるのかは今は不明だが・・・。
  • dbCreate はテーブルの作成・削除有無を制御する。
    • create-drop: アプリケーションの起動ごとにDrop-Createする、正常シャットダウン時にはDropする。
    • create: アプリケーションの起動ごとにDrop-Createする。正常シャットダウン時にはそのまま残す。
    • update: アプリケーションの起動ごとに再作成しない。存在しない場合のみ作成する。正常シャットダウン時にはそのまま残す。
    • アプリケーション起動時なのか、初回アクセス時なのか不明確。
  • environment/development/datasource/dbCreate = "update" にしてみたが、揮発状態は変わらず。environment/development/datasource/url = "jdbc:hsqldb:mem:devDB" がin-memoryデータベース指定だから変わるわけもなく。
dataSource {
    pooled = true
    driverClassName = "org.hsqldb.jdbcDriver"
    username = "sa"
    password = ""
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}
// environment specific settings
environments {
    development {
        dataSource {
            dbCreate = "create-drop" // one of 'create', 'create-drop','update'
            url = "jdbc:hsqldb:mem:devDB"
        }
    }
    test {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:mem:testDb"
        }
    }
    production {
        dataSource {
            dbCreate = "update"
            url = "jdbc:hsqldb:file:prodDb;shutdown=true"
        }
    }
}
DataSource を変更して、揮発しないDBにしてみる(HSQLDB ファイル編)

mkdir /grails/db でDB保存先を作成し、下記のように変更してみると、再起動しても揮発しない状態にできた。

// environment specific settings

    development {
        dataSource {
            //dbCreate = "create-drop" // one of 'create', 'create-drop','update'
            //url = "jdbc:hsqldb:mem:devDB"
            dbCreate = "update"
            url = "jdbc:hsqldb:file:/grails/db/devDB"
        }
    }