2009年12月22日火曜日

Quartzプラグインで予約と取消


grails create-app quartz-app
cd quartz-app
grails install-plugin quartz
grails create-job sample
grails create-controller schedule


やりたいのは任意のタイミングで実行するジョブの予約と取消。
grails-app/jobs/SampleJob.groovy

class SampleJob {
static triggers = {
}
def execute(context) {
println "execute: $context"
}
}

空のtriggersを定義しないとデフォルトで1分ごとに設定されるので注意。

grails-app/jobs/ScheduleController.groovy

class ScheduleController {

def index = { }

def jobManagerService

// 5秒後にSampleJobを実行するように予約する。
def schedule = {
def scheduleDate = new Date()
use(org.codehaus.groovy.runtime.TimeCategory) {
scheduleDate += 5.second
}
def trigger = new org.quartz.SimpleTrigger('myTriggerName', "myTriggerGroup", scheduleDate)
try {
SampleJob.schedule(trigger) // 返却値はscheduleDate
flash.message = 'schedule success'
} catch (org.quartz.ObjectAlreadyExistsException e) {
flash.message = 'schedule failure'
}
redirect(action: index)
}

// 5秒以内なら上の予約を取り消す。
def unschedule = {
if (jobManagerService.unscheduleJob ("myTriggerGroup", 'myTriggerName')) {
flash.message = 'unschedule success'
}
else {
flash.message = 'unschedule failure'
}
redirect(action: index)
}
}

scheduleアクションでは実行前で予約済みのトリガ名を登録すると例外が発生するで連続で二回すると例外が発生する。unscheduleアクションでは、予約取消できない場合はfalseが返ってくる。

grails-app/views/schedule/index.gsp

<html>
<head>
<title>Welcome to Grails</title>
<meta name="layout" content="main" />
</head>
<body>
<g:if test="${flash.message}">
<div class="message">
${flash.message}
</div>
</g:if>
<div class="dialog" style="margin-left:20px;width:60%;">
<ul>
<li class="controller">
<g:link controller="schedule" action="schedule">schedule</g:link>
</li>
<li class="controller">
<g:link controller="schedule" action="unschedule">unschedule</g:link>
</li>
</ul>
</div>
</body>
</html>

実行すれば試せます。

grails run-app

2009年11月14日土曜日

Grails + CAS、CASサーバ編 2

CASクライアントについてまとめる予定だったけど、予定を変更してCASサーバの非SSL化を先にまとめる。証明書の発行が面倒だとかいう理由らしい。

前回の設定に加えて、2つファイルを編集する。
まず、casサーバのURLを変更する。cas-server-3.3.4/cas-server-webapp/src/main/webapp/WEB-INF/cas.propertiesのsecurityContextまわりをhttps://localhost:8443からhttp://localhost:8080に変更する。

cas.securityContext.serviceProperties.service=http://localhost:8080/cas/services/j_acegi_cas_security_check
cas.securityContext.casProcessingFilterEntryPoint.loginUrl=http://localhost:8080/cas/login
cas.securityContext.ticketValidator.casServerUrlPrefix=http://localhost:8080/cas

次に、CASが落とすCookieをSSL通信でなくても送信するように変更する。
cas-server-3.3.4/cas-server-webapp/src/main/webapp/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xmlのp:cookieSecure="true"p:cookieSecure="false"に変更する。

<bean id="ticketGrantingTicketCookieGenerator"
class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
p:cookieSecure="false"
p:cookieMaxAge="-1"
p:cookieName="CASTGC"
p:cookiePath="/cas" />

2009年11月7日土曜日

Grails + CAS、CASサーバ編

CASでSSOやることになったのでメモ。
ユーザ情報は既存のユーザ管理アプリのDBを使用する。

おっきく分けて3つ。
  1. Grails Spring Security Pluginでユーザ管理アプリを作る
  2. CASサーバの構築
  3. Grails Spring Security PluginでCASクライアント

3つめは次回。

ということで、まずは「Grails Spring Security Pluginでユーザ管理アプリを作る」は本題ではないのでちゃちゃっと作る。

grails create-app ums
cd ums
grails install-plugin acegi
grails create-auth-domains
grails generate-manager

データベースはpostgresql 8.3。
データベースユーザは"sa"でパスワードなし。
データベースはlocalohst:5432:umsdb
ユーザはPersonのまま(テーブル:person、カラムはusername、passwd、enabledなど)

次に、今回のメイン「CASサーバの構築」。やることは
  1. ダウンロード、展開
  2. ファイルを2つ編集
  3. ビルドして配備
まずはCAS ServerをJASIG Communityから落としてくる。
http://www.jasig.org/cas/download

今回使うのはCAS Server 3.3.4 Final。
cas-server-3.3.4-release.zipを落として展開する。

で、cas-server-3.3.4/cas-server-webapp/pom.xmlを編集、61行目のldapをjdbcにして、ついでにJDBCドライバとデータソース用のライブラリも追加しておく。

<dependency>
    <groupId>org.jasig.cas</groupId>
    <artifactId>cas-server-support-jdbc</artifactId>
    <version>${project.version}</version>
</dependency>
<dependency>
    <groupId>postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>8.3-603.jdbc3</version>
</dependency>
<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.2</version>
</dependency>

cas-server-3.3.4/cas-server-webapp/src/main/webapp/WEB-INF/deployerConfigContext.xmlを編集、データソースとユーザテーブルの情報を設定する。
データソースのbeanを定義して
92行目のSimpleTestUsernamePasswordAuthenticationHandlerをSearchModeSearchDatabaseAuthenticationHandlerに差し替える。ユーザテーブル、ユーザIDカラム、パスワードカラム、パスワードのエンコード、データソースを設定してあげる。

<bean class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler">
    <property name="tableUsers"><value>person</value></property>
    <property name="fieldUser"><value>username</value></property>
    <property name="fieldPassword"><value>passwd</value></property>
    <property name="dataSource" ref="dataSource"/>
    <property name="passwordEncoder">
        <bean class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"> 
            <constructor-arg value="SHA1" />
        </bean>
    </property>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName">
        <value>org.postgresql.Driver</value>
    </property>
    <property name="url">
        <value>jdbc:postgresql:umsdb</value>
    </property>
    <property name="username"><value>sa</value></property>
    <property name="password"><value></value></property>
</bean>

maven2でビルドする。

cd cas-server-3.3.4/cas-server-webapp/
mvn package


最後にTomcatに配備してhttp://localhost:8080/cas/login/にアクセスして確認しておk。

これでCASサーバの準備ができたので、次回はCASクライアント編。
JDBCTicketRegistryについても調査済みなので次々回あたりでまとめる。

参考にしたページ:
JDBC - CAS User Manual - JA-SIG Wiki

2009年10月30日金曜日

Grailsの新しい本が出てた

Grails: A Quick-Start Guideを買いました。

ついでにGrails in Actionも買っちゃいました。

で、ざっと見た感想です。

Grails: A Quick-Start GuideはなんていうかStart Guideでした。例に習ってけばいい感じに身に付く感じ。はじめての人はこちらな感じがしました。

Grails in Action はどちらかというとUser Guideな感じ? 本家のUser Guide + いくつかプラグインの使い方という印象を受けました。

Grailsに触れてみたいならGrails: A Quick-Start Guide
Grailsに慣れてきたらGrails in Action
日本語がいいならGrails徹底入門

2009年10月28日水曜日

Custom Constraintsを使ってみた

制約を自由に追加できるプラグインがリリースされたので早速使ってみた。

grails install-plugin constraints

とりあえずalpha(英字のみ許す)制約で作成する。

grails create-constraint alpha

するとgrails-app/utils/AlphaConstraint.groovyが生成される。同時にテストケースも作成される。あとはvalidatorを書く感じにvalidateクロージャを実装する。

class AlphaConstraint {

def defaultMessageCode = 'default.not.alpha.message'

def supports = { type ->
return type!= null && String.class.isAssignableFrom(type)
}

def validate = { propertyValue ->
propertyValue ==~ /[A-Za-z]+/
}
}

defaultMessageCodeを定義すると、定義したキーでメッセージが使える。
supportsはどの型用の制約かを定義する。

メッセージ定義に以下を追加する。埋め込みオブジェクトは、プロパティ名、クラス名、プロパティ値、制約のパラメタ。

default.not.alpha.message=Property [{0}] of class [{1}] with value [{2}] must be alphabet

これでドメインクラスに制約として使える。今までvalidatorに実装してたのも使い回しができるようになって最高です。validatorと違うところはメッセージキーを返せないところ。validateクロージャの実行結果がBoolean(boolean)にならないとClassCastExceptionが発生した。
使い方はこんな感じ。

class Book {

def title

static constraints = {
title(blank: false, alpha: true)
}
}

つづいて他のプロパティを参照する系を試してみる。
例としてleProperty(他のプロパティ以下であること)制約を作る
他のプロパティを参照する場合は、やっぱりvalidatorの実装と同様に2つ目のパラメタを用意してあげればいい。

class LePropertyConstraint {

def defaultMessageCode = 'default.not.leProperty.message'

def validate = { propertyValue, target ->
target."$params" ? propertyValue <= target."$params" : true
}

}

paramsは制約のパラメタ。
使うときは以下のような感じ。

class Project {
Date dateStarted
Date dateEnded
static constraints = {
dateStarted(leProperty: 'dateEnded')
dateEnded()
}
}

制約を修正するとリロードするけど、その制約を評価したときに例外が発生する。そこは今後に期待です。

2009年10月27日火曜日

Searchableプラグインであいまい検索

saveでindex作成するときにSynonymLookupProviderが提供するシノニムをindexに混ぜ込むかんじ。Searchable.groovyにURLが張ってあるからその通りにやってみた。


grails create-app myapp
cd myapp
grails install-plugin searchable
grails install-searchable-config
grails create-domain-class book
grails create-controller book


grails-app/conf/Searchable.groovy

compassSettings = [
'compass.engine.analyzer.myAnalyzer.filters':
'myAnalyzerfilter',
'compass.engine.analyzerfilter.myAnalyzerfilter.type':
'synonym',
'compass.engine.analyzerfilter.myAnalyzerfilter.lookup':
'sample.MySynonymLookupProvider'
]


src/groovy/sample/MySynonymLookupProvider.groovy

package sample

import org.compass.core.CompassException
import org.compass.core.config.CompassSettings
import org.compass.core.lucene.engine.analyzer.synonym.SynonymLookupProvider

class MySynonymLookupProvider implements SynonymLookupProvider {

String[] lookupSynonyms(String string) {
def synonyms = [string]
if (string == 'artifact') {
synonyms << 'artefact'
}

return synonyms as String[]
}

void configure(CompassSettings settings) throws CompassException {
}

}

grails-app/domain/Book.groovy

class Book {

String title

static searchable = {
analyzer 'myAnalyzer'
// title(analyzer: 'myAnalyzer') カラムごとに指定するなら
}

}


これで new Book(title: 'artifact').save() すると、artefactで検索に引っかかるようになる。日本語でやるならSen入れないとだめぽい。