[Grails]バッチを作成する（run-script）

#概要
Grailsでバッチなどを作成する場合、当然既存のGORMの機能を利用したい。
そこで、`run-script`というコマンドを使うことでお手軽に実現できます。

#使い方
$PROJECT_HOME/src/groovyディレクトリ配下にスクリプトを記述する。
スクリプト内では、普通にGORMを利用できます。（当然ドメインのimportは必要）
実行する時は以下のようにします。

`grails run-script src/groovy/hogepackage/Hoge.groovy`

DateSource.groovyで独自の環境（dev、test、prod以外）を定義していて、それを利用したい場合は以下のように`-D grails.env=xxx`オプションを利用します。
`grails -Dgrails.env=originalEnvironment run-script src/groovy/hogepackage/Hoge.groovy`

# 引数の渡し方
バッチなので当然コマンドライン引数を渡したい！けど、実行するスクリプトの後ろになにか文字列を渡すと、それもバッチファイルとして実行しようとするので、単純に引数をスペースで区切って渡す、と言う事ができません。
そこで、Javaでおなじみのシステムプロパティー（ **-D** オプション）を利用します。

`grails run-script -Dfoo=123 -Dbar=abc src/groovy/hogepackage/Hoge.groovy`

上記のコマンドを実行すると、Hoge.groovyの中では`System.getProperty()`で値を取得できます。

```groovy:src/groovy/Hoge.groovy
class Hoge {
    static main(args){
        println "Hello Batch"
        println System.getProperty("foo")    //123
        println System.getProperty("bar")    //abc
    }
}
```

#Serviceを利用する方法
スクリプトの中でServceを使うにはちょっとクセのある方法を利用します。

利用するServiceは以下。

```groovy:HogeService.groovy
package test
import grails.transaction.Transactional

@Transactional
class HogeService {

    def serviceMethod() {
        println "This is Service!!"
    }
}
```

で、実際にServiceを利用するスクリプトは以下。

```groovy:src/groovy/Hoge.groovy
import test.HogeService
class HogeBatch {

    def HogeBatch(HogeService hogeService) {
        println "HogeBatch Object"
        hogeService.serviceMethod()
    }
}

// どっちでもOK
//def hogeService = ctx.hogeService
def hogeService = ctx.getBean("hogeService")

def obj = new HogeBatch(hogeService)
```

**ctx** (application context)からhogeServiceを取得します。
後は通常通りServiceを利用できます。

実行コマンド：`grails run-script src/groovy/Hoge.groovy`
実行結果：

```
| Running script src/groovy/Hoge.groovy ...
HogeBatch Object
This is Service!!
| Script src/groovy/Hoge.groovy complete!
```

#run-scriptで利用するServiceについて余談
run-scriptは、GroovyShellを利用して指定されたスクリプトを実行します。
実装は以下のようになっています。

```groovy
// RunScript.groovyの該当箇所
def shell = new GroovyShell(classLoader, new Binding(ctx: appCtx, grailsApplication: grailsApp))
shell.evaluate script.getText('UTF-8')
```


GroovyShellに **ctx** がBindingされていますが、 **classの中ではBindingされた値を読み込む事が出来ません。**
そのため、HogeBatchクラスの中で **ctx** を参照しようとすると以下のようなエラーが発生します。

`Error Error executing script RunScript: No such property: ctx for class: Hoge (Use --stacktrace to see the full trace)`
run-scriptで実行されるスクリプトで、classの中でServiceを利用したい場合は、今回のように **class宣言外** で一旦必要なServiceを取得しておいて、classに渡してあげる必要が有ります。
そのため、今回はHogeBatchクラスの外でServiceを取得して、それをHogeBatchクラスに渡して上げるようにしています。

Grailsには直接関係ないのですが、今回の場合スクリプトファイル名を **Hoge.groovy** としています。
で、そのHoge.groovyの中で、classの宣言外、もしくはclass宣言をせずに直接コードを記述した場合、Groovyは自動的にファイル名と同名のクラスを生成してそのクラスの中にコードを入れ込みます。
そのため、classと、class宣言外のコードが混在するコードの場合、class宣言外のコードの為にファイル名と同名のクラス/インスタンスが生成されるため、明示的に宣言するclass名には、ファイル名とは異なるクラス名を指定する必要が有ります。
この辺りの話しは[ここ](http://www.oki-osk.jp/esc/groovy/#6)が非常に分かりやすいです。

今回のHoge.groovyであれば、

```groovy
// どっちでもOK
//def hogeService = ctx.hogeService
def hogeService = ctx.getBean("hogeService")

def obj = new HogeBatch(hogeService)
```
がクラスの宣言外に有る為、自動的にファイル名と同じ **Hoge** というクラスが生成されます。
`class HogeBatch {...}`は当然 **HogeBatch** というクラスが生成されます。

#注意点
1.インタラクティブモードでは実行できません。
2.実行時に、DataSource.gorvyは読み込まれますが、Bootstrap.groovyは読み込まれません。（実行されない）
3.通常のGroovyスクリプトとは、実行される際の挙動が少し違う。
例えば以下のようなソース

```groovy:Test.groovy
class Test {

    static main(args) {
        println "main"
    }

    def Test() {
        println "constructor"
    }
}
```
これを普通に　`groovy Test.groovy`と実行すると、当然コンストラクタは呼ばれていないので、結果は **main** と表示される。
しかし、今回のようにgrailsのバッチとして `run-script` を通して実行すると、 **コンストラクタが先に実行されて、その後mainが実行される。**
呼んでないのになんでコンストラクタが実行されるんだ。。。。
おそらくGroovyShellがソースを読み込んでいるところでnewしているんだろうなと邪推。（未調査）

#参考URL
[6 スクリプトとしてのプログラムと暗黒面](http://www.oki-osk.jp/esc/groovy/#6)
[Grailsでrun-scriptコマンドを使って任意のGroovyスクリプトを走らせ、その中でServiceを使う](http://grails.hatenablog.com/entry/2013/07/08/130342)