カスタム検索

2009年1月31日土曜日

Grails-1.1 Beta3の検証

Grails-1.1Beta3がリリースされたのでリリースノートから一部検証・・・

・Persistence of Collections of Basic Types
hasManyでStringとかが指定出来るようになりましたよ。

class Book {
static hasMany = [authors:String]
}


hsqlDBのfileで確認したところ以下のようなテーブルが出来るらしい
CREATE MEMORY TABLE BOOK_AUTHORS(BOOK_ID BIGINT,AUTHORS_STRING VARCHAR(255),CONSTRAINT FKCBA3F8F23FA913A FOREIGN KEY(BOOK_ID) REFERENCES BOOK(ID))
でもscaffoldには対応してくれないみたい。現段階では正直微妙?

・Persistence of Collections of Enum Types
 上のhasManyでEnumが指定できるよ。

enum VehicleStatus { "NONE", "LITTLE", "MANY" }
class Book {
static hasMany=[statuses:VehicleStatus]
}


以下のようなテーブルが出来るっぽい
CREATE MEMORY TABLE BOOK_STATUSES(BOOK_ID BIGINT,VEHICLE_STATUS VARCHAR(255),CONSTRAINT FKA209D4B63FA913A FOREIGN KEY(BOOK_ID) REFERENCES BOOK(ID))
同じくscaffoldには対応してない

・Read-Only Access to Objects
ReadOnlyでデータをひっぱれるよ。

def book = Book.read(1)


通常Grailsではひっぱったドメインはフィールドの値を変更してコントローラーを抜けると、変更された値が自動的に保存されます。
readでひっぱるとフィールドの変更があっても元のデータは更新されません。
んがっ、book.save()したら更新されちゃいました :p
あとfindとかしたい場合はどうするんだろう

・Default Sort Order
ドメインのデフォソートを決められるよ。

class Book {
static hasMany=[authors:Author]
static mapping = {
authors sort:"name"
}

String title
}

class Author {
static belongsTo = [book:Book]
String name
}


今までは book.authors を参照した場合順番はランダムだったのがソートフィールドを指定出来るようになりました。
上の例だとauthorのname順で取得出来ます。
サンプルではドメイン自体のsortも決められるような事がかいてあったけど .list() では確認できませぬでした。 

2009年1月29日木曜日

Flexのちょいネタ

MXMLのclickとかにfunctionを当てる場合の話


<mx:Button click="hoge()" />


と書く所を


<mx:Button click="{hoge()}" />


と中括弧でかこってやると、FlexBuilderがちゃんと関数として解釈してくれるので、CMD+Clickでfunctionまで飛べるお。

俺たちのBirtはこれからだ!

今日はBirtでシコシコと帳票を作っておりますた。

元になるデータを起こすところまではまーまー順調に解消。
さて、このテンプレを元にデータを流すわけですが・・・
今持っているデータだけではさっぱり足りずにフィールドを増やしフォームを増やしとあれやこれやであっという間に時間が。

AirからHTMLをオープン、HTMLからGrailsへアクセスして、Grailsでデータをこねこね、XMLにして返したのをさらにBirtで微調整。
なんかもうエディタがいっぱい開いて目的のソースがどこにあるのかわかりませんよっと。

しかしEclipseのBirtPluginはMacで動かしているせいなのか(?)微妙に動きが悪くてしょんぼりです。
・ラベルで日本語打つ>エンター>消える・・・(ペーストしてエンターだといける)
・データバインディングを変更する>変わらない・・・(セレクタをカーソルで切り替えてエンターだといける)
帳票上のデータが多いと目に見えて重くなるし、ちょっと困ったちゃんです。

それでもフリーでスクリプトやらなんやらバリバリかけてGUIの帳票ツールまであるのは凄い事ですね。

2009年1月27日火曜日

Air(Flex)か〜ら〜のHTMLか〜ら〜のJavascript

AirのHTMLタグから、パッケージに含んだファイル(とかローカルファイルとか)を読み込む方法



var file:File = new File("app:/html/index.html");
var src = new File(file.nativePath).url;
html.location=src;

<mx:HTML id="html" />


パッケージ内にあるJavaScriptは相対パスで読めます。
HTMLとinclude.jsが同じ場所にあるとすれば以下のように。

<script src="include.js"></script>


JavaScriptが全然別のドメインとかでもひっぱてきて使えちゃいますよ。

<script src="http://hoge.jp/outer.js"></script>

なんでJavaScriptのコアをどっかにおいておけばアプリ本体を更新せずにサーバのJSを置き換えて更新も出来ちゃう。

でもってAirからHTMLでロードした中のJavaScriptの実行方法
htmlのcompleteイベントからdomを取得出来るので、、、

private function loadCompleteHandler(e:Event):void {
dom=e.currentTarget.domWindow;
}
<mx:HTML id="html" location="{src}" width="100%" height="100%" complete="loadCompleteHandler(event)" />


domの直下にスクリプトがバインドされているので、それを実行すればオッケィ

html.functionName();

2009年1月23日金曜日

httpBuilderで文字化けする件について

httpBuilderでgoogleのサイトを取得している時はうまくいってたのですがShift_JISのサイトを取り込もうとしたらUTF-8になってしまいました。
まぁよくある事だと思ってソースをさぐっていったのですが、思ったよりも深い所で大変でした、、、

結論からいくとContentTypeが text/html,text/plain,text/xml などの場合は、 ParserRegistry というクラスの parseText という所で、 HttpResponse から charset をあてて InputStream にして返しています。
この charset をあてる所がくせもので、 HttpResponse に含まれるパラメーターの charset を使い、なければ OSデフォ(Charset.defaultCharset().name())をあてるという処理になっています。
おそらくテストしたサイトの response に charset のパラメーターがついていなくて、UTF-8があたったっぽいです。

とりあえず parseText に無理矢理 charset をのせてやったらうまくいきました :)
でもちゃんと作るなら ParserRegistry に charset をあてれるように拡張しないといかんとですね。

2009年1月19日月曜日

httpBuilderが俺にもっと輝けと囁いている

httpBuilderという素敵なものがあったので試してみた

allをダウンロードして、target/dependenciesのjarとhttp-builder自体のjarをlibへコピっと。

Basic認証して、htmlを取得するサンプル。
取得したhtmlはxmlSlurperへ変換し操作しやすいように。


import groovyx.net.http.*
import static groovyx.net.http.Method.*
import static groovyx.net.http.ContentType.*

// basic認証用にbase64したID/Passを作成
def testBytes = "user:pass".getBytes("ISO-8859-1")
def encoded=testBytes.encodeBase64()

def http = new HTTPBuilder("http://hogehoge.jp/requiredBasicAuth")
http.headers=["Authorization":"Basic ${encoded}"]

try {
http.get(contentType:TEXT) { resp, reader ->
def page = new XmlSlurper(new org.cyberneko.html.parsers.SAXParser()).parseText(reader.text)
def data = page.depthFirst()
data.each { println it }
}
} catch(Exception e) {
println "auth failure"
}


headerの調整とかpost指定とかなしで、簡単にとりたいだけだったら、上のCyberNekoのSlurperを使っている所を使うと簡単

def page = new XmlSlurper(new org.cyberneko.html.parsers.SAXParser()).parse("http://www.google.co.jp")
def data = page.depthFirst()
data.each { println it }

こんだけでオッケー!

getのオプションで使う query がうまく動かなかったのが気になる所・・・

2009年1月14日水曜日

iPhoneでJsonをごにょごにょ

簡単なJSONを返すアクションを作成

def json = {
def jsonData=[["name":"任天堂","hard":"wii"],
["name":"sony","hard":"PlayStation3"],
["name":"Microsoft","hard":"XBOX360"]]

render(text:jsonData as JSON)
}


ブラウザで確認すると、こんな感じのJSONデータが返ります。

[{"name":"任天堂","hard":"wii"},{"name":"sony","hard":"PlayStation3"},{"name":"Microsoft","hard":"XBOX360"}]


これをiPhoneでうけとってTableViewに表示します。
JSONを取得する部分

- (void)getJSON {
NSURL *jsonURL = [NSURL URLWithString:@"http://localhost:8080/jsonSample/test/json"];
NSMutableString *jsonData = [NSMutableString stringWithContentsOfURL:jsonURL encoding:NSUTF8StringEncoding error:nil];
[jsonData replaceOccurrencesOfString:@"'"
withString:@"\""
options:NSCaseInsensitiveSearch
range:NSMakeRange(0,[jsonData length])];

NSLog(jsonData);

if (jsonData == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Error" message:@"JSON Dataの取得に失敗しました。" delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
} else {
SBJSON *json = [SBJSON new];
json.humanReadable = YES;

id result = [jsonData JSONValue];
NSLog([json stringWithObject:result error:NULL]);

webResult = [[NSMutableArray alloc] init];

NSEnumerator* enumerator;
enumerator = [result objectEnumerator];

// while文を使って要素にアクセスする
id obj;
while (obj = [enumerator nextObject]) {
[webResult addObject:obj];
}
printf("データ件数 %d",[webResult count]);
}
}


基本は前と一緒です。シングルクォートをダブルクォートに置換したりしてます。
webResultはクラスのフィールドです。
取得した値をループし、webResultへ追加していきます。

InterfaceBuilderでTableViewをおいて、Outletとdelegateを設定します。



dataSource、delegateをFile'sOwnerに設定し、クラスに定義したtableViewと連結します。
クラスにtableView用の定義をします。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [webResult count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
cell = [[UITableViewCell alloc] init];
cell.text = [[webResult objectAtIndex:indexPath.row] objectForKey:@"name"];
return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"あなたが選んだのは"
message:[[webResult objectAtIndex:indexPath.row] objectForKey:@"hard"]
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles: nil];
[alert show];
[alert release];
}


上から順番に
numberOfRowsInSection : テーブルに何件あるかを返すメソッド
cellForRowAtIndexPath : 1行毎に表示するメソッド
didSelectRowAtIndexPath : 行を選択した時のアクション
です。

詳細はメモなんで略 :p

2009年1月12日月曜日

よろしい、ならばiPhoneでJSONだ その3

Grailsのアプリと通信してJSONデータを取得するサンプルです。

Grails側にJSONデータを返すアクションを用意します。

def json = {
def jsonData=["任天堂":"wii","sony":"PlayStation3","Microsoft":"XBOX360"]

render(text:jsonData as JSON,contentType:"application/x-json")
}

contentTypeは application/json でも無指定でも読み込める。

Objective-C側

NSURL *jsonURL = [NSURL URLWithString:@"http://localhost:8080/jsonSample/test/json"];
NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL encoding:NSUTF8StringEncoding error:nil];

if (jsonData == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Webservice Error" message:@"JSON Dataの取得に失敗しました。" delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];
} else {
SBJSON *json = [SBJSON new];
json.humanReadable = YES;

id jsonItem = [jsonData JSONValue];
NSLog([json stringWithObject:jsonItem error:NULL]);
}

NSString の initWithContentsOfURL で encoding を指定するのがポイントです。
encodingがあってないと nil になって通信出来てないのかと思って悩んでました・・・

そろそろインターフェース側の処理をやっていこうかな :)

よろしい、ならばiPhoneでJSONだ その2

Objective-CでJsonを取り扱うスニペットめもりんぐ

NSDictionaryとNSStringをいったりきたり


// NSDictionaryからStringに
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
@"The iPhone", @"Today",
@"The World", @"Tomorrow",
nil];
SBJSON *json = [SBJSON new];
json.humanReadable = YES;
NSLog([json stringWithObject:dict error:NULL]);

// StringからNSDictionaryに
NSString *jsonString = @"{\"hoge\":\"foo\",\"wow\":[\"good\",\"cool\"]}";
id jsonObj = [json objectWithString:jsonString error:NULL];
if(jsonObj==NULL || jsonObj==nil) {
printf("NULL dayo\n");
} else {
NSLog([json stringWithObject:jsonObj error:NULL]);
}


jsonでパース出来るのは、ダブルクォートだけです、シングルコートはだめです。(これにずっと気づかなかった・・・)
objectWithStringの返り値はNSArrayだったりNSDictionaryだったりするので、idで受け取る必要があります。

2009年1月6日火曜日

glassfishでメモリチューニングシンドローム

めもめもっと

ドメイン毎に設定ファイルがありやんす。
glassfish/domains/domain1/config/domain.xml

java-configタグ以下に jvm-options タグを書いていく。
デフォは -client とか入ってるのとメモリまったく調整してないのでパツパツ落ちます。

一例

<java-config classpath-suffix="" debug-options="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9009" system-classpath="">
<jvm-options>-server</jvm-options>
<jvm-options>-XX:+UnlockDiagnosticVMOptions</jvm-options>
<jvm-options>-XX:+LogVMOutput</jvm-options>
<jvm-options>-XX:LogFile=${com.sun.aas.instanceRoot}/logs/jvm.log</jvm-options>
<jvm-options>-Djava.endorsed.dirs=${com.sun.aas.installRoot}/lib/endorsed</jvm-options>
<jvm-options>-Djava.security.policy=${com.sun.aas.instanceRoot}/config/server.policy</jvm-options>
<jvm-options>-Djava.security.auth.login.config=${com.sun.aas.instanceRoot}/config/login.conf</jvm-options>
<jvm-options>-Xmx2048m</jvm-options>
<jvm-options>-Xms2048m</jvm-options>
<jvm-options>-Xmn1024m</jvm-options>
<jvm-options>-XX:PermSize=128m</jvm-options>
<jvm-options>-XX:MaxPermSize=128m</jvm-options>
<jvm-options>-XX:NewSize=320m</jvm-options>
<jvm-options>-XX:MaxNewSize=320m</jvm-options>
<jvm-options>-XX:SurvivorRatio=2</jvm-options>
<jvm-options>-XX:TargetSurvivorRatio=70</jvm-options>
<jvm-options>-Djavax.net.ssl.keyStore=${com.sun.aas.instanceRoot}/config/keystore.jks</jvm-options>
<jvm-options>-Djavax.net.ssl.trustStore=${com.sun.aas.instanceRoot}/config/cacerts.jks</jvm-options>
<jvm-options>-Djava.ext.dirs=${com.sun.aas.javaRoot}/lib/ext${path.separator}${com.sun.aas.javaRoot}/jre/lib/ext${path.separator}${com.sun.aas.instanceRoot}/lib/ext${path.separator}${com.sun.aas.derbyRoot}/lib</jvm-options>
<jvm-options>-Djdbc.drivers=org.apache.derby.jdbc.ClientDriver</jvm-options>
<jvm-options>-Dcom.sun.enterprise.config.config_environment_factory_class=com.sun.enterprise.config.serverbeans.AppserverConfigEnvironmentFactory</jvm-options>
<jvm-options>-XX:NewRatio=2</jvm-options>
<jvm-options>-Dconfluence.disable.peopledirectory.anonymous=true</jvm-options>
</java-config>

よろしい、ならばiPhoneでJSONだ

GoogleCodeにてObjective-CのJSON Frameworkが公開されています。
http://code.google.com/p/json-framework/

これを使ってiPhoneでJSONを使用してみましょう。
以下の記事を参考にしています。
http://iphone.zcentric.com/2008/08/28/using-threads-for-a-json-request/

まずはJSON Frameworkのインストールです。
GoogleCodeからdmgをダウンロードします。
ユーザのLibraryフォルダへSDKsフォルダをコピーします。
(ユーザが hoge だった場合 > /Users/hoge/Library に dmg内の SDKs をコピー)

xcodeを立ち上げて適当な iPhone プロジェクトを生成します。
メニューから、「プロジェクト」>「プロジェクト設定を編集」 で 追加SDK へ以下の設定を追記します。

$HOME/Library/SDKs/JSON/$(PLATFORM_NAME).sdk



「他のリンカフラグ」 へ以下の設定を追記します。

-ObjC -ljson

あとはJSONを使用するコードで JSON/JSON.h をインポートしてやればOKです。

上記サイトにサンプルアプリもアップされています。
以下サンプルアプリを実行した画面です。