カスタム検索

2008年12月31日水曜日

iPhoneで動かすHelloWorld その4

インフルエンザで死んでおります・・・胃が痛い(T_T)
高熱でちょっと忘れかけておりますが、日々の積み重ねが大事です!

ではViewControllerの実装です。

MyViewController.m で警告が出ているはずです。
MyViewController.h で property 指定した textField , label , string に対するメソッドがないという事なので、アクセサメソッドの実装をしてやります。


@synthesize textField;
@synthesize label;
@synthesize string;


これらのインスタンス変数が正しく解放されるように dealloc に追記します。


- (void)dealloc {
[textField release];
[label release];
[string release];
[super dealloc];
}


ボタンを押させると changeGreeting がコールされる所まで確認しましたので、ここでテキストフィールドから文字列を取得し、ラベルを書き換えるようにします。


- (IBAction)changeGreeting:(id)sender {
self.string=textField.text; // textFieldから文字列を取得し、自身のstring変数に格納します。

NSString *nameString=string;
if([nameString length]==0) {
nameString=@"World"; // textFieldの文字列が空なら World を代入する
}
NSString *greeting=[[NSString alloc] initWithFormat:@"Hello, %@!",nameString]; // ラベルに表示するメッセージを作成
label.text=greeting;
[greeting release];
}


ここまででビルドすると、以下のような結果が得られます。



大筋は完了しましたが、キーボードが閉じられない事に気がつくでしょう。
MyViewController.m に以下のコードを追記します。


- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
if(theTextField==textField) {
[textField resignFirstResponder];
}
return YES;
}


再度実行すると、Doneでキーボードが隠れる事を確認できます。

以上で完成です!思ったより長かったです・・・体が痛い(T_T)

2008年12月27日土曜日

iPhoneで動かすHelloWorld その3

Viewに物をおいてみましょう!

ControllerView.xib をインターフェースビルダーで起動して、LibraryからTextField と Label と Round Rect Button を配置します。
↓こんな感じです。



各Objectの設定はInspectorパネルから色々操作出来ます。
TextのInspectorを見ると、TextInputTraitsという項目があって、ここでキーボードの形や、決定ボタンの形などが調整出来ます。

ここでシミュレーターで実行してみると、キーボードは開きますが、消す事が出来ない状態です。
これはキーボードの終了イベントを受け取る場所がないのでキーボードが閉じられないというわけです。

このイベントをMyViewControllerと関連付けします。
関連付けはアウトレットというインスタンス変数によって行われます。

MyViewController.h

#import

//MyViewControllerはテキストフィールドのデリゲートになるため、UITextFieldDelegateプロトコルを採用する必要があります。
//プロトコルの指定はクラスの継承元クラスの後に <> で指定します。
@interface MyViewController : UIViewController {

//IBOutletは、続く宣言をアウトレットとして使う。という指示です。
IBOutlet UITextField *textField;
IBOutlet UILabel *label;
NSString *string;
}

//@propertyでgetter,setterをproperty定義します。
@property (nonatomic, retain) UITextField *textField;
@property (nonatomic, retain) UILabel *label;
@property (nonatomic, copy) NSString *string;

//Helloボタンを処理するchangeGreetingメソッドを定義します。
//IBActionは、続く宣言をターゲット/アクション接続のアクションとして扱う。という指示です。
-(IBAction)changeGreeting:(id)sender;

@end


実装クラスへメソッドの中身を定義します。

MyViewController.m

- (IBAction)changeGreeting:(id)sender {
printf("call!");
}


保存してインターフェースビルダーへ戻り、File's OwnerのIdentity Inspectorをみると、
Class ActionとClass OutletsにchangeGreetingとlabel,textFieldが追加されています。
これはMyViewControllerクラスがControllerViewのビュークラスとして指定されている為です。

インターフェース上に設置されたオブジェクトとフィールドを関連付けます。
File's Ownerを右クリックして出てきたリストから○をドラッグして、関連付けたいオブジェクトへドロップします。

ボタンのアクションをファンクションへ関連付けます。
ボタンをCTRL+クリックで出てくるInspectorの中からTouch Up Insideの○をFile's Ownerへドラッグ&ドロップします。
changeGreetingが表示されるのでそれを選択する事で関連付けが行われます。

テキストフィールドのデリゲートを設定します。
テキストフォールドをCTRL+ドラッグでFile's Ownerへドロップします。
選択肢からDelegateを選ぶ事でデリゲートの設定が行われます。

ここまで設定したら一度実行してみます。
ボタンを押してコンソールにcall!が表示されたら連携がうまくいっています!
次は実際に処理を書いていきます。(まだまだキーボードは戻せません :p)

2008年12月25日木曜日

iPhoneで動かすHelloWorld その2

Viewを追加するにはViewControllerとnibファイルを使用します。

まずはViewControllerの作成です。
Xcodeから新規ファイルで CocoaTouchClasses の UIViewControler subclass を選択します。
テンプレートを元にざっくりとファイルが生成されます。

作成したクラスをアプリケーションデリゲートのインスタンス変数に追加します。

作成したクラスをMyViewControllerとし、HelloWorldAppDelegateへ以下のように定義します。

HelloWorldAppDelegate.h

#import
@class MyViewController; // クラス宣言

@interface HelloWorldAppDelegate : NSObject {
UIWindow *window;
MyViewController *myViewController; // 追加したビューをインスタンス変数として定義します
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) MyViewController *myViewController;

@end


HelloWorldAppDelegate.m

#import "HelloWorldAppDelegate.h"
#import "MyViewController.h" // インポート宣言

@implementation HelloWorldAppDelegate

@synthesize window;
// アクセサメソッドの合成?
@synthesize myViewController;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
// ViewControllerの作成、ControllerView.nibをターゲットとします。bundleはresouceの位置的な情報?
MyViewController *aViewController = [[MyViewController alloc]initWithNibName:@"ControllerView" bundle:[NSBundle mainBundle]];
// デリゲータのmyViewControllerに上で作成したインスタンスを指定
self.myViewController=aViewController;
// 一時インスタンスの解放
[aViewController release];

// myViewControllerからviewを取得
UIView *controllersView=[myViewController view];
// windowへControllerViewのviewを追加
[window addSubview:controllersView];

[window makeKeyAndVisible];
}

- (void)dealloc {
[myViewController release]; // デリゲートが解放される時(アプリ終了時)にビューを一緒に解放します
[window release];
[super dealloc];
}

@end


ビューを作成します。
InterfaceBuilderを起動して新規作成>CocoaTouch>Viewでビューが作成できます。
ControllerView.nib としてHelloWorld プロジェクトに保存します。
Xcodeが立ち上がっていれば、自動的にリソースとして取り込むか選択が出ます。
リソースとして登録されたらResourcesフォルダに移動します。

このViewがどのコントローラークラスに所属するかを指定します。
Tools > Identity Inspector を起動して、File's Ownerを選択>ClassにMyViewControllerを指定します。
File's Ownerにアウトレット(nibファイル内の項目に接続しているインスタンス変数)を設定します。
File's OwnerをCtrlクリックしながらViewまでドラッグすればOKです。

Tool > Attribute Inspector から BackgroundColorを変更してシミュレーターで動かすと、このViewが正しく読み込まれているのがわかります。

次はビューにオブジェクトを追加します。

2008年12月23日火曜日

iPhoneで動かすHelloWorld その1

とりあえずHelloWorld的なものを作ってみよう!
とはいってもDeveloperCenterのiPhoneアプリケーションチュートリアルを実行しながらメモるだけですが :)

まずは最低限必要なCocoaのデザインパターンの説明をざっくりと

・Delegation
オブジェクトが、デリゲートとして指定された別のオブジェジェクトに定期的にメッセージを送信し、入力の要求やイベントの発生を通知するデザインパターン。
このデザインパターンはクラス継承の代替手段として、再利用可能なオブジェクトの機能を拡張するために使用する。

使用される場所:メイン処理の終了感知、ビューのセットアップ&管理、テキストフィールドのReturnイベント処理等

・Model-View-Controller
いわゆるMVC。
Model=データ、オブジェクト
View=ユーザインターフェース部分
Controller=ModelとViewを仲介する部分

・Target-Action
コントロールオブジェクトから、別のオブジェクトにメッセージの送信と、送信されたメッセージを受け取って処理する仕組み。

使用される場所:ボタン押す>Controllerへ指示


次に、Xcodeを起動して新規プロジェクトの作成を選択。
Windows-Based Applicationを選択。
とりあえず「ビルドして進行」するとiPhoneシミュレーターが立ち上がって白い画面が出たらOK

まずはどのようにアプリケーションが起動して画面が出て行くのかを追ってみよう!
main.m をみるとUIApplicationMainを起動しているだけなのがわかりました。
UIApplicationMainは info.plist 内の MainNibFile 値のインターフェースを起動します。
デフォではMainWindowになっているので、MainWIndow.xibが読み込まれます。
(前回はここの関連が?だった)

MainWindowをダブルクリックするとインターフェースビルダーが起動します。
そこに含まれる4つの項目は以下のような意味をもっています。
・File'sOwner
 UIApplicationのインスタンスです。
・First Responder
 イベント処理、今はまだ使用しません。
・[アプリケーション名]AppDelegate
 アプリケーションのデリゲートインスタンス
・WIndow
 実際の画面に表示されるウインド

アプリケーションは起動が完了すると applicationDidFinishLaunching をデリゲートに送信します。
デフォルトで作成されるこのメソッド処理を見てみます。

- (void)applicationDidFinishLaunching:(UIApplication *)application {

// Override point for customization after application launch
[window makeKeyAndVisible];
}

windowを定義してるだけですね。

次回はここへ、デフォルト以外のViewを追加してみます。

2008年12月21日日曜日

Objective-Cにおけるメモリ管理なんて怖くない!その2

前回、retain , release と dealloc でインスタンスのメモリ管理をやりました。
deallocで関連するインスタンスの解放をするのは良いとしても、 release 自体も忘れてしまったりする事があります。

Objective-Cでは autorelease という仕組みがあるそうなのでそこを見ていきます。
NSAutoreleasePool クラスを使って実現します。
NSAutoreleasePool は、自動解放されるべきインスタンスを保持しておくクラスです。
クラスが定義されると、 autorelease対象の広場(プール)が作成されます。
広場が存在する状態で、他クラスのインスタンスに autorelease を送ると、そのインスタンスは広場にほうりこまれます。
(広場に存在するだけで参照されているわけではないので、インスタンスのリファレンスカウンタは変わらない)

広場の release を実行すると、広場に存在するインスタンスすべてに release が実行されます。


id pool=[[NSAutoreleasePool alloc] init]; // 広場の作成

id foo=[[Foo alloc] init]; // Fooクラスのインスタンス作成。foo のリファレンスカウンタは1
[foo autorelease]; // foo インスタンスを広場に登録する。リファレンスカウンタは1のまま

[pool release]; // foo にもreleaseが実行される


NSAutoreleasePool は、複数存在してもよい。
複数存在する場合に autorelease を実行した場合は、一番近い所で指定されたプールへほうりこまれる。
基本は開始と終了でメインの pool を作り、内部でループなど一時的なインスタンスを作成する場所で子プールを作成する。


id mainPool=[[NSAutoreleasePool alloc] init];

id hoge=[[Hoge alloc] init];
[hoge autorelease]; // mainPool へ登録される

while(条件) {
id subPool=[[NSAutoreleasePool alloc] init];

id foo=[[Foo alloc] init];
[foo autorelease]; // subPool へ登録される

[subPool release];
}

[mainPool release];


CocoaのGUIアプリケーションはNSApplicationがイベント発生時(マウスクリック等)に子プールを作成して、イベント終了時にreleaseしてくれるので、それほど気にしなくても自動でやってくれる。
iPhoneSDKもそうなのかな?


・オーナーシップとメモリ管理
init~~ で呼ばれるメソッドはオーナーシップが定義される。(メモリの管理をする必要がある)
同等のメソッドで init がない場合は一時的なインスタンスとなり、 autorelease に登録される。

例:文字列を扱うNSStringクラスの場合

-(id)initWithUTF8String:(const char *)bytes
+(id)stringWithUTF8String:(const char *)bytes

どちらも文字列を保持するオブジェクトが作成されるが、initは生成したオブジェクトがオーナーとなり、
stringはプールへ登録される(autoreleaseが実行される)

オーナーシップを与えないコンストラクタをコンビニエンスコンストラクタと呼ぶ。

・シングルトンの定義
定数やシングルトンを作成する為には、retain,release,retainCountの機能を上書きする必要がある。
retainとreleaseを何もしないメソッドにし、retainCountは常に UINT_MAX を返すようにする。

2008年12月20日土曜日

Objective-Cにおけるメモリ管理なんて怖くない!

Object-C2.0でのメモリ管理には2種類の方法がある。

・リファレンスカウンタ方式
・ガーベジコレクション

ガーベジコレクションは2.0から採用された自動メモリ管理の仕組みで、Javaのガーベジコレクションと同じ要領である。
一定時間毎に不必要な(参照されていない)クラスを探しだし、解放していく。
これは後にとっておくとして・・・

まずはリファレンスカウンタ方式から詳しく見ていく。
一言でいうと、クラスがいくつ参照されているかをカウンタで管理し、0になったらメモリを解放するという仕組みである。

具体的には alloc init された時点で、インスタンスはカウンタを1とする。

以下例:

id foo = [[Foo alloc] init]; // fooインスタンにカウント1が刻まれる
[foo release]; // fooインスタンスのカウントは0となり、メモリがリリースされる

id foo2 = [[Foo alloc] init]; // foo2インスタンスにカウント1が刻まれる
id hoge = [[Hoge alloc] init];
[hoge setFoo:[foo2 retain]]; // HogeインスタンスにはsetFooでfooを参照する機能がある。この時点でfoo2インスタンスにカウント2が刻まれる

[foo2 release]; // foo2のカウントが1に減る
[foo2 release]; // foo2のカウントが0となり、メモリがリリースされる


・retain メソッドでカウントを+1する。このメソッドは自身を返す。
・release メソッドでカウントを−1する。
・現在のカウント数は retainCount メソッドで見る事が出来る。
・dealloc でメモリをリリース出来るが、これはカウンタに関係なくリリースされてしまうので使うべきではない。

でもいちいち手動でリリースしてたらきりがない!
そこで dealloc をオーバーライドして、インスタンスが解放される時にインスタンスが保持するオブジェクトをリリースするのだ。

以上をふまえて簡単なテストプログラム

ReferenceCounterクラス

#import

@interface ReferenceCounter : NSObject {
id obj;
}
-(void)setObj:(id)_obj;
@end


@implementation ReferenceCounter
-(void)setObj:(id)_obj {
obj=[_obj retain]; // 何かオブジェクトがセットされる。ただ代入するのではなく、使用するよ!とカウンタを+する。
}

-(void)dealloc {
[obj release]; // 自身がリリースされるまえに、自分がもってるオブジェクトをリリースする。
[super dealloc]; // 自身のリリース
}

@end


int main(int argc, char *argv[]) {

id foo=[[NSObject alloc] init];
printf("retainCount: %d\n",[foo retainCount]);

id rctest=[[ReferenceCounter alloc] init]; // テストクラスを作成
[rctest setObj:foo]; // テストクラスになんかオブジェクトをセットする
printf("retainCount: %d\n",[foo retainCount]);

[foo release]; // オブジェクトをリリース
printf("retainCount: %d\n",[foo retainCount]);

[rctest release]; // テストクラスをリリース

return 0;
}


・foo インスタンスが alloc されて foo = 1 カウンタがつく
・rctest インスタンスが alloc されて rctest = 1 カウンタがつく
・rctest に foo をセットする。setter内で foo を retain するので foo=2 になる
・foo インスタンスを release する。 foo=1 になる
・rctest をリリースする。rctest=0になり、dealloc がよばれ rctest内のobj(foo)がreleaseされる。 foo=0になる

出力は以下のような感じ
retainCount: 1
retainCount: 2
retainCount: 1

なるほど、オブジェクト思考っぽい。
この考えは普通にJavaでも活かせる気がする!

2008年12月18日木曜日

不思議の国のアプリ(iPhone)

えー、メモリ管理をすっとばして、とりあえず今まで学んだ知識でiPhoneアプリのサンプルがどれぐらい理解できるか挑戦!

XCODEの新規プロジェクトからNavigation-BasedApplicationを作成してみる。

まずは main.m から


#import

int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}


あらスッキリ。
NSAutoreleasePoolでメモリ管理をしつつ、UIApplicationMainってのを起動しているだけっぽい?
ではUIApplicationMainを・・・って早速ここからどこにいってるのか・・・

そんなわけでiPhoneDeveloperCenterのGettingStartedを一生懸命斜め読み
どうやらUIApplicationMainからはMainWindow.xibが開かれるって事かな?
xibを開くとインターフェースビルダーが立ち上がる、、、おお、なんかおもしろい
して、このボタンのイベントなどはどこで設定するのやら・・・
うーん何がなんだかサッパリ、、、日本語の情報がほしー
DeveloperCenterの情報は豊富だけど、有料でもいいから日本語がほっすいよん
とりあえず来月日本語本出るみたいだし読んでみようかなー

覚えておくと便利なConfluenceのウラワザ

Confluenceの2.9.2を入れて数日、日本語で検索をした時にエラーが出ます。

エラーメッセージを見ると StringIndexOutOfBounds エラーが出ているもよう。

・英語での検索はエラーが出ない
・前のバージョンと比べて、検索結果でハイライトされるようになった

以上の結果から検索結果の文字をハイライトする時の文字数計算が2バイト考慮されてなくてエラーが出てるのではないかと推測

まずは検索結果の decorators/components/search-results.vmd を見てみる
該当部分は以下

<ul class="search-results">
#foreach($searchResult in $results)
<li>
#searchResult($searchResult $showExcerpts $queryString)
</li>
#end
</ul>


searchResultを消してしまうと検索結果がでないので、この searchResultがどこにあるかを探っていくと、以下にたどりついた
template/includes/macros.vm
該当部分は以下

#set ($summary = $generalUtil.makeSummary($contentBody, $queryString))
#elseif ($searchResult.type == "attachment" && $contentBody)
#set ($summary = $generalUtil.makeSummary($contentBody, $queryString))
#elseif ($searchResult.type == "attachment" && $searchResult.comment)
#set ($summary = $generalUtil.makeSummary($searchResult.comment, $queryString))


GeneralUtilクラスのmakeSummaryがダメダメっぽい

APIを見てみる > http://docs.atlassian.com/com/atlassian/confluence/atlassian-confluence/latest/com/atlassian/confluence/util/GeneralUtil.html

Summary型を返さないといけないので、ここをはぶいてしまうと検索は出来るけど、検索結果がページタイトルしかでなくなってしまう
makeSummaryを見てみると、 content だけをうけとるのと、 contentとqueryを受け取るメソッドがあるのがわかる。
queryのハイライトが問題なんで、contentだけを渡すメソッドに書き換えてみる。


#set ($summary = $generalUtil.makeSummary($contentBody))
#elseif ($searchResult.type == "attachment" && $contentBody)
#set ($summary = $generalUtil.makeSummary($contentBody))
#elseif ($searchResult.type == "attachment" && $searchResult.comment)
#set ($summary = $generalUtil.makeSummary($searchResult.comment))


ここのテンプレートは再起動しないと反映されないので、confluenceを再起動。
検索してみると、無事検索がとおり、検索結果にページタイトルと中身の一部が表示された! :D

2008年12月17日水曜日

いまさら聞けない「Objective-C」超入門 Objective-C その4

そんなこんなで第4回。

動的結合ってなんじゃ?
・存在しないメソッドを実行しようとしたコードを書いた場合に、コンパイル時にエラーが出ず、実際に実行したときにメソッドが実行出来るかを確認する方式を「動的結合」と呼ぶ
・Javaは比較的静的結合、Groovyみたいにダイナミックに扱う場合は動的結合、という認識でいいのかな?

Object は id として扱うとしてきたが、普通にクラス名で宣言してもよい

Class Fooがあったとして
id c;
c=[Foo alloc];

Foo *c;
c=[Foo alloc];
どちらでもFooのインスタンスを作成出来る。

クラス名をしておくと、不正なクラス利用をされていた場合にコンパイル時にチェックしてくれる。
ダイナミックにクラスが変わる場合は id を使うって事ね!

nilについて
・nilはポインタが空の状態、NULLとは違う
・Classのinitに失敗した場合等に nilとなる
・JavaでNullになんかすると NullPointerException が出るが、 nil にメッセージを送ってもエラーにならない(ただしプログラム上ではわかりやすくする為に nil 判定を書いておく事は重要)

インターフェース内のクラス指定について
Objectの中に別のClassを持つ場合、 importして書く事も出来るが、通常は @class 宣言を使う。


@class Hoge;

@interface Foo : NSObject {
Hoge *ho;
}


HogeはClassだよと教えてやるわけです。ただし実装部では import する必要があります。

変数hogeにgetter , setter を定義する場合
-(int)hoge;
-(void)setHoge:(int)value;
getterにはgetを付けない!

Class型って?
・インスタンス化されたものじゃなくて、元となるクラス
・NSObjectに用意された class を呼ぶと、そのクラスのClass型を取得できる
これを使って動的にクラスを割り当てるサンプル!

Class theClass = flag ? [Foo class] : [Hoge class];
id v = [theClass alloc];


flagがtrueなら v はFooのインスタンスになり、falseなら v はHogeのインスタンスとなる。

インスタンスのクラスが指定したクラスのインスタンスかをチェックするには isMemberOfClass を使う。
BOOL isFoo = [v isMemberOfClass:[Foo class]];
v がFooのインスタンスであれば isFoo は true となる。

クラスメソッドとは?
・Javaでいうところのstaticメソッド?
・NSObjectには、class とか isMemberOfClass とか、alloc がクラスメソッドとして定義してあるため、上記のように使える
・自分でクラスメソッドを定義するには、今まで - と書いてた所を + と書く
例:allocの定義
+(id)alloc;

クラス内から、自分のクラスメソッドを呼ぶとき等は、
[[Foo alloc] say];
と書いてしまうと、Fooを継承したクラスで不都合が起きてしまうので
[[[self class] alloc] say];
とすれば、必ず呼ばれた時のクラスのクラスメソッドを呼ぶ事が出来る。

今日はこの辺りで・・・、次回はやっとメモリ関係

2008年12月16日火曜日

いまさら聞けない「Objective-C」超入門 Objective-C その3

そんなこんなで継承について

スーパークラスとかサブクラスとか継承の考え方はJavaとまったく一緒、省略
Cocoa環境では基本的にNSObjectというのがルートクラスとなる。JavaでいうObject的なものっぽい。
C++にはこういったすべてのベースとなるルートクラスがないらしい(!)

では実際に以下のような継承クラスを作成してみる
Parent = ルートクラス callNumber(int) で value に値をセットし、受け取った値を返す
Child = Parentのサブクラス、callNumber(int) をオーバーライドし、 受け取った2倍の値を返す
Grandchild = Childのサブクラス、callNumberでセットされた value の3倍の値を返す anotherNumber メソッドを追加

Parent.h

#import

@interface Parent : NSDocument {
int value;
}
- (int)callNumber:(int)num;

@end


Parent.m

#import "Parent.h"

@implementation Parent
- (int)callNumber:(int)num {
value=num;
return num;
}

@end


Child.h

#import
#import "Parent.h"

@interface Child : Parent {
}
-(int)callNumber:(int)num;

@end


Child.m

#import "Child.h"

@implementation Child
-(int)callNumber:(int)num {
value=num;
return num * 2;
}

@end


Grandchild.h

#import
#import "Child.h"

@interface Grandchild : Child {
}
-(int)anotherNumber;

@end


Grandchild.m

#import "Grandchild.h"

@implementation Grandchild
-(int)anotherNumber{
return value * 3;
}
@end


そしてこれらを実行して結果を出力する main.m
せっかくなんで入力された文字のキーコードを出力する q で終了


#import
#import "Parent.h"
#import "Child.h"
#import "Grandchild.h"

int main(void)
{
id p,c,g;
char buf[8];

p=[Parent alloc];
c=[Child alloc];
g=[Grandchild alloc];

while(scanf("%s",buf) > 0) {
switch(buf[0]) {
case 'q':return 0; break;
default:
printf("parent class >> %d\n",[p callNumber:buf[0]]);
printf("child class >> %d\n",[c callNumber:buf[0]]);
[g callNumber:buf[0]];
printf("grandchild class >> %d\n",[g anotherNumber]);
break;
}
}

return 0;
}


今日のポイント
Javaと違う所!
interfaceはクラスには必ず存在し、Javaのように同じインターフェースを持った別のクラスみたいな考えではない
同じような事をやる場合はインターフェース用のクラスを作成する、もちろんクラスだからインターフェースもいる
インターフェース用クラスのインターフェースとかどんだけ〜

なんで import は h ファイルだけでいいのか、昨日の謎がとけた!
早くぐりぐり動かしたいな〜

2008年12月15日月曜日

いまさら聞けない「Objective-C」超入門 Objective-C その2

とりあえず前回動かしたクラスを別々のファイルにする。

インターフェース部を クラス名.h でヘッダファイルとして、実装クラスを クラス名.m とするのが一般的らしい。
クラス名.m には クラス名.h を import する。
別の実装を各場合も クラス名.h を import すればおk。

main.h 内は main だけ残して、Helloクラスを別ファイルにする
main から import するのは クラス名.h だけでおk。何でだろう?同クラス名をデフォの実装として読み込んでくれる?

Hello.h


#import

@interface Hello : NSDocument {
int val;
int min,max,step;
}
- (id)initWithMin:(int)a max:(int)b step:(int)s;
- (int)value;
- (id)up;
- (id)down;
@end



Hello.m


#import "Hello.h"

@implementation Hello
- (id)initWithMin:(int)a max:(int)b step:(int)s {
self=[super init]; // NSObjectの初期化を呼ぶ
if(self!=nil) { // nilはnullみたいなもん? init が成功していたら
val=min=a;
max=b;
step=s;
}
return self;
}

- (int)value {
return val;
}

- (id)up {
if((val += step) > max) val=max;
return self;
}

- (id)down {
if((val -= step) < min) val=min;
return self;
}
@end



main.m


#import
#import "Hello.h"

int main(void)
{
id v,w;

v=[[Hello alloc] initWithMin:0 max:10 step:2];
w=[[Hello alloc] initWithMin:0 max:9 step:3];
[v up];
printf("%d %d\n",[v value], [w value]);
[v up];
[w up];
printf("%d %d\n",[v value], [w value]);

return 0;
}




Objective-CだからといってすべてをObject指向で作る必要はない
Object型より関数型の方が軽いため、あきらかな重い処理は関数型で作成した方が良い
関数定義のみを含むソースは .c ファイルとして扱う
ただしオブジェクトを引数や返り値としたり、メソッド呼び出しなどをする場合は .m とする

#import は複数のクラスを読み込んだ場合、かぶるヘッダファイルを読み込まない
#include は読み込む。(通常はヘッダファイル内で2度読み込まないように定義しておく)
2度読み込みたい事情が無い限り #import でおk

今日はここまで!次回はクラス継承

2008年12月14日日曜日

いまさら聞けない「Objective-C」超入門

というわけでObjective-Cはじめます。初心忘れるべからず。

・はじめに Objective-Cとはなんじゃらほい
 C言語にオブジェクト指向の機能を追加した言語
 MacOSX上で動作する言語、それ以外ではあまり使われない。iPhoneのアプリもObjective-Cで作成する 。
 C++もCにオブジェクト指向を追加した言語だが、Objective-Cとは別路線で互換性は無い
 Objective-Cの特徴としては、際立って柔軟性が高く、効率的なコードを書く事が出来るらしい
 Leopardの登場とあわせてObjective-C2.0(ガベージコレクション、宣言プロパティ、高速列挙を追加)が公開&実装された



なんとなく概要がわかった所でまずは触ってみよう

//オブジェクトは id という特殊な型
id obj

//オブジェクトにメッセージを送る(オブジェクトのメソッドを実行する?)
//この形をメッセージ式と呼ぶ。
[obj msg]

//objのmsgを呼び出した結果が戻り値となる
//以下のように、戻り値を使って別のメッセージを呼ぶ事も出来る
[[obj msg] msg2];
[[[obj msg] msg2] msg3];

// [] は配列としても使う、左側にあるもので自動的に判別される
array=[1,2,3]; // 配列の定義
el=test[[obj msg]]; // test配列の添字として [obj msg] の返り値を使う

//メッセージに引数が必要な場合はコロンを付ける
[obj msg:'yossy']

//メソッド一覧はメッセージセレクタと呼ばれる
//コロンの有無でも別のセレクタとして認識される
//コロンが引数的なキーワード
copy
copy:
delete
allDelete
create
findBookName:Author:

//クラスのインスタンス化
[クラス名 alloc]

//通常はinitするべし
//initがデフォ、initからはじまるメソッドでも良い
[[クラス名 alloc] init]

//インターフェースの作成
@interface Hello:NSObject
{
//インスタンス変数の宣言
}
// メソッドの宣言
- (id)delegate;
- (id)cellAtRow:(int)row column:(int)col;

@end

// this は self と表現される
//文字列を扱うのはちょっと面倒?
//とりあえず入門書に従って数値をいじるクラスが出来た・・・今日はタイムアップなり
// XCodeで新規Cocoaアプリケーション、mainに全部かきかき



#import

@interface Hello : NSDocument {
int val;
int min,max,step;
}
- (id)initWithMin:(int)a max:(int)b step:(int)s;
- (int)value;
- (id)up;
- (id)down;
@end

@implementation Hello
- (id)initWithMin:(int)a max:(int)b step:(int)s {
self=[super init]; // NSObjectの初期化を呼ぶ
if(self!=nil) { // nilはnullみたいなもん? init が成功していたら
val=min=a;
max=b;
step=s;
}
return self;
}

- (int)value {
return val;
}

- (id)up {
if((val += step) > max) val=max;
return self;
}

- (id)down {
if((val -= step) < min) val=min;
return self;
}
@end

//int main(int argc, char *argv[])
int main(void)
{
id v,w;

v=[[Hello alloc] initWithMin:0 max:10 step:2];
w=[[Hello alloc] initWithMin:0 max:9 step:3];
[v up];
printf("%d %d\n",[v value], [w value]);
[v up];
[w up];
printf("%d %d\n",[v value], [w value]);

// return NSApplicationMain(argc, (const char **) argv);
return 0;
}

2008年12月10日水曜日

報道されないConfluenceの裏側

Confluenceの移行をしていてハマったのでめもめも

・Confluenceはメジャーバージョンが変わると復元が出来ない
・フルバックアップが2GBを超える場合は処理出来ない(おいおい)

さて困った

とりえあずスペースでバックアップして復元してみると、メジャーバージョンが違うからだめだよ。と・・・
バックアップファイルを解凍して中を覗いてみると、exportDescriptor.properties の buildNumber というそれっぽいものを発見
とりあえずこれを新しいバージョンのbuildNumberにかえてインポートしてみる・・・できた! :D

ユーザとかはDBから直接もってきたりしたけどNGだった :(
同じユーザIDでユーザを作ってやると履歴とかはちゃんと表示されたので、ちょっと安心

しかし無理矢理感がいなめない・・・なんか正しい移行方法があるのかぁ

人生に役立つかもしれないpostgresについての知識

今日もまたよくやるけど、よく忘れるメモ

postgresで外部から接続出来るようにする(ローカル内のpgAdminとかからサックリ操作出来るようにする)

pg_hba.conf でアクセス制限をつける。
下の方にある行を書き換える

サンプル


# "local" is for Unix domain socket connections only
local all all trust
# IPv4 local connections:
host all all 127.0.0.1/32 trust
host all all 192.168.0.0 255.255.255.0 trust


日本語訳:とりあえず 192.168.0 シリーズからと、ローカルからなんでもばっちこいやー

postgresql.conf の設定


#---------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#---------------------------------------------------------------------------

# - Connection Settings -

listen_addresses = '*' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost', '*' = all


の部分の listen_addresses がデフォだと localhost になるんで、ここのコメントアウトをはずし、 * (すべて許可)にする

で、postgresを再起動してやればOK

2008年12月9日火曜日

いとしさと切なさとCnetOS on Confluence 

CentOSにGlassFishのせてConfluenceしてみよう

まずjdk5インスコ

jdk-1_5_0_16-linux-i586-rpm.bin
を落としてくれば実行するだけでおk

デフォのjava1.4をリプレイスしとく
mv /usr/bin/java /usr/bin/java_14
ln -s /usr/java/jdk1.5.0_16/bin/java /usr/bin/java

JAVA_HOMEの設定も忘れずに

お次にGlassFishインスコ
http://download.java.net/glassfish/v3-prelude/release/glassfish-v3-prelude-ml.zip
をダウンロードしてきて、適当な場所に解凍
解凍したフォルダ/bin/asadmin start-domain
で起動

でもってConfluenceインスコ
最新版は2.10ですが、日本語環境は2.9なので、2.9をダウンロードします
http://www.atlassian.com/software/confluence/downloads/binary/confluence-2.9.1.tar.gz
日本語化パッケージ
http://www.atlassian.co.jp/software/confluence/confluence-ja-2.9.0.jar

解凍したら confluence/WEB-INF/lib に上記日本語パッケージをコピーします

confluence/WEB-INF/classes/confluence-init.properties
で、データ保存場所を指定
sh build.sh
を実行すると dist/confluence-2.9.war が作成される。

作成されたファイルを glassfish へデプロイする
cp dist/confluence.war /usr/local/glassfish/glassfish/domains/domain1/autodeploy
この時 war の名前がコンテキスト名になるので、好きな名前に変えておくよろし

http://server:8080/confluence
にアクセスするとConfluenceのセットアップウィザードが立ち上がれば後は煮るなり焼くなり!

今日のCentOSスレはここですか

CentOSをインストール、サーバにしたいのでGUI系は全部カット

よくやるけど結構忘れるファイヤーウォールの設定、めもめも

/etc/sysconfig/iptables

のファイルに追記する。

-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
で排除されるので、この行より前に

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
みたいな感じで指定する。↑は port 80 なので http サーバ用

/etc/init.d/iptables restart
で即反映、しなくても再起動したら反映される :)

2008年11月18日火曜日

この春はゆるふわ愛され電子ペーパーでキメちゃおう☆

http://release.nikkei.co.jp/detail.cfm?relID=205413&lindID=4
紙に近い安価な電子ペーパーだそうで・・・

新聞を広げる光景がレアになる日も近いのでしょうか
電子ペーパーをiPhoneのようにめくるとかステキ

2008年10月20日月曜日

どう書く?

どう書くネタで盛り上がったので、更にのってみました。

ものすごいハイウエスト日記

短くするのは得意じゃないんで、Groovyっぽい感じで


// 各行を処理するクロージャー
def changeLine1={ line,val ->
return line[2]
}
def changeLine2={ line,val ->
return line[1]
}
def add={line,val ->
return val.isNumber() ? val.toInteger()+1 : val
}

// CSV読み込み&変換
def convClo=["",changeLine1,changeLine2,add]
def ret=[]
def headers
new File("test.csv").splitEachLine("\t"){ line ->
if(!headers) headers = line
def lineData=[]
line.eachWithIndex { val,idx ->
lineData << (convClo[idx] ? convClo[idx].call(line,val) : val)
}
ret << lineData
}

// ソート関係
def sort="ID" // SortField
def order="ASC" // ASC | DESC
def headerIdx=0
headers.eachWithIndex{ val,idx ->
if(val==sort) headerIdx=idx
}

// 出力
println ret.remove(0).join("\t")
ret=ret.sort{it[headerIdx].isNumber() ? it[headerIdx].toInteger() : it[headerIdx]}
if(order.equalsIgnoreCase("DESC")) ret=ret.reverse()
ret.each{ println it.join("\t") }


後で増やしたり差し替えたり出来るように、クロージャーでデータの処理をわけてます。
(でもchangeLine1とchangeLine2は統合されるべきだよなぁ・・・)
ソートもフィールド名と ASC | DESC 与えて結果が変えられるようになってます。

と、思ったらもう答えが出ているわけで。

2008年10月19日日曜日

オデッセイ納車!



はい、嘘です。

試乗にいってまいりました。
正直雑誌で初めて見た時は「微妙?」と思ったんですが、実際に見たら以外に良いですね。
内装に関してはちょっと物足りない感がありましたが、走りはさすがHONDAといった感じです。
1-2速が低速重視になったらしくて、5ATなのにもの凄くスムーズに力強い加速でした、もうCVTいらないんじゃないかと・・・

と、久しぶりのブログが車だけではあれなんで、Grailsネタ

・2つのデータソースを使う

旧システムや、他システムとの連携で2つのDBへ接続したい時があると思います。
大規模なものであれば、連携用のGrailsアプリを用意して、Webサービスで連携といったほうが確実なのですが、
ちょっと使いたいだけの場合には以下の方法もありかと思います。

GrailsはSpringFrameworkを使っているので、SpringのbeanとしてDatasourceを注入してやればOKです。

grails-app/conf/spring/resources.groovy に以下のような記述をします。


beans = {
def properties = {
def driver = "jdbc.driver.url"
driverClassName = driver
url = "jdbc.url"
username = "user"
password = "pass"
}

def otherDataSource= otherDataSource(BasicDataSource, properties)
otherDataSource.destroyMethod = "close"
}


これで otherDataSource として別のBasicDataSourceがbeanとして注入されました。

これをControllerやServiceなどに注入して使います。


class OtherDataSourceService {
boolean transactional = true
def otherDataSource // これで注入されます。

def test() {
def conn = otherDataSource.getConnection()
def stmt = conn.prepareStatement("SELECT * FROM Book")
def result = stmt.executeQuery()

}
}


これで ResultSet が返ってくるので、これを煮るなり焼くなりしちゃいましょう。
もちろん UPDATE や INSERT も使えます。
これだけではGORMには参加出来ませんので、ドメインと連携したりしたいのであればもっと深い所までいかないといけませんが、
そこまでいくのであれば前述したように、連携アプリを用意してしまったほうが良いと思います :)

2008年8月4日月曜日

「Grails+BlazeDS」を鍛える

Grails+BlazeDSでデータを取得する時の話

・hasManyしているドメインをとろうとすると、型をキャストしようとした所でFlexからSessionナイヨーって怒られる

基本lazyモードなんで、データを取得しようとした時にFlex側からHibernateのSessionがとれないっぽい
web.xmlに以下の内容を追記してやる事で解決


<filter>
<filter-name>sessionView</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>sessionView</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


他にも criteria を使って eager で読み込んでやるとか、domainでeager にしておくとかでも良い、メモメモ

def criteria = Item.createCriteria()
def items = criteria.listDistinct{
fetchMode('contents', FM.EAGER)
}


・hasManyしたフィールドの扱い

hasManyしたドメインはarrayCollectionとしてくるので、Flex側のクラスを合わせておく。


public var contents:ArrayCollection;


他にも grails的にくっつくものをつけておくと、警告が出なくて幸せ。

public var id:int;
public var version:int;
public var metaClass:Object;


・hasManyしたドメインがオートキャストされない

Object扱いになっている、自前でキャストしてやる事は可能

for(var c:Number=0;item.contents.length > c; c++) {
var contents:Content = Content(item.contents[c]);
}

なぜかこのコードは書いておくだけで、実行される前にキャストされている。謎い。キモい。

2008年7月17日木曜日

BlazeDSヤバイ。まず広い。

grails + blazeDs なテスト

・適当なアプリを作成
grails create-app flexremote
cd flexremote

・flex pluginをインストール
grails install-plugin flex

・サービスを作成


class BookService {
static expose = ['flex-remoting']

def getBooks() {
return Book.list();
}
}


expose = ['flex-remoting']
にしておくと flex plugin が自動的に amf を配備してくれる。
これをFlex(AIR)側 remoteObject として定義してやると、サービスの中のメソッドが自由に呼べるようになる。


<mx:RemoteObject id="srv"
destination="bookService"
endpoint="http://localhost:8080/flexremote/messagebroker/amf"
result="resultHandler(event)"
fault="faultHandler(event)" />

<mx:Script>
<![CDATA[
srv.getProducts();
]]>
</mx:Script>



desitination はサービス名、endpoint に amf のURLを指定。
GrailsのDomainと同じフィールドを持つ class を作成しておけば、自動的にバインドされてハッピー。

ココにすばらすぃサンプルがあります。

さっくり速度検証もしてみました。
10000件のデータを同じGrailsアプリから blazeDS , WebService , Http(JSON)で投げてみました。

結果は blazeDS の圧勝。

WebServiceも通信部分だけなら早かったんですが、Modelにバインドすると結局 blazeDS の倍ぐらいかかります。
JSONは通信もバインドもかなり差をつけられた感じです、、、
まぁモデルをStringに置き換えてStringをパースして更にモデルにしてと明らかにステップ数が多いのでしょうがないですね。

Grailsでファイル監視

特定のフォルダを一定時間毎に監視して処理をするサンプルです。

・適当なアプリを作成します。
grails create-app qtest

・監視には quartz を使いますので、quartz plugin をインストールします。
cd qtest
grails install-plugin quartz

・quartz plugin が提供するコマンドを使って、適当な job を作成します。
grails create-job searchFile

grails-app/jobs/SearchFileJob.groovy が作成されます。
class名が @artifact.name @ になっているので、これを SearchFileJob に直し、監視設定を記述します。


class SearchFileJob {
 // ここは将来的にダイナミックに定義できるようになるらしい
 static triggers = {
  // 起動から3000ミリ秒後に処理を開始、1024ミリ秒毎にexecute()を実行
  simpleTrigger startDelay:3000, timeout:1000
 }

 def execute() {
  // この中が実行される
 }
}


・次に実際の処理を記述していきます。
指定したフォルダに .txt ファイルをみつけると、別のフォルダに移動してくれるという単純なサンプルを作ってみます。
execute() を以下のようにします。


def execute() {
// 監視するパスを設定
def _path = new File("/opt/job")
def _dest = new File("/opt/job/dest")

// パスが存在し、かつディレクトリであるか
if(_path.exists() && _path.isDirectory()) {
// フォルダ内のファイルを取得し、最終更新日が古いものからループをかける
def list = _path.listFiles()
list.sort{ it.lastModified() }.each { _file ->
// ロックファイルでないか確認
if(!_file.getName().toLowerCase().endsWith(".lock")) {
// 拡張子が .txt のものであれば処理をする
if(_file.getName() =~ ".*\\.[Tt][Xx][Tt]") {
// ロックファイルがあるか確認
def lock = new File(_file.getAbsolutePath()+".lock")
if(!lock.exists()) {
// ロックファイルを作成
lock.write("locked")

// ここに実際の処理を書く
def _endFile = new File(_dest,_file.getName())
_file.renameTo(_endFile)

// 処理が終わったらロックファイルを削除する
if(_endFile.exists()) {
lock.delete()
}
}
}
}
}
}
}


途中で ファイル名+.lock のファイルを作成していますが、
これは一回の処理に時間がかかる場合や、監視プログラム自体をクラスタ化した場合に、
同じファイルを二度処理しないように判別する為に使っています。
クラスタ化しない場合には変数で管理しても良いです。

他のクラス同様、クラスの注入を行えるので、 def grailsApplication を記述しておけば Config.groovy からのロードなんかも出来ます。

2008年5月29日木曜日

Grails on GlassFish

というわけで GlassFish に Grails プロジェクトをのっけてみました。
以前 WebSphere で痛い目にあっていたのでちょっと不安になりながら作業を進めたのですが、、、
びっくりするぐらいあっさりと動いてしまいました。
色々と変わった事をしているアプリだったのでうまくいかないかなーと思ったんですが、さすが公式にサポートしてるというだけはある感じです。
インストールやら設定やらも簡単だし凄くいい感じ♪

ひとつ Servlet から Content-Type を指定せずに response してる所で、
GlassFish だと自動的に text/html charset=iso-8859-1 (だったかな?)が付与され、文字化けしてました。
Tomcat だと無いならないで Content-Type をはかないのでブラウザで吸収され問題なかったので気づきませんでした。
これは Servlet 側の問題なので Servlet に正しい Content-Type をセットしてやる事で無事解決しました :)

2009/6/23 追記
Servletを通すレスポンスの場合はHttpServletRequest の setCharacterEncoding か setContentType を使ってエンコードを指定して下さい。
Servletを通さない場合(Grailsだとweb-app以下に置いてあるようなファイル、jsonとかxmlとかtextとかを読みたい場合)は、以下にデフォの設定個所があります。
設定>server-config>HTTPサービス の HTTPプロトコル>強制タイプとデフォルトタイプを指定して下さい。

2008年4月8日火曜日

Grails Code Reading

そんなわけで Grails Code Reading 第10回を担当する事になりました。
お題はRichUI。最近色々触っているので、それらをGrails上で使ってみたって感じでいこうかと・・・
とりあえあずGWT、GWT-EXT、AIR、RichUIPluginあたりでいいのかなーと思ってるのですが、なんかこうインパクトがほしいなぁ
まぁでも時間もないのでやれる範囲でやってきます |-`)/

2008年3月30日日曜日

シビックにて初サーキット

友達の走行会イベントで行ってきました、モーターランド三河
グリップ20台、ドリフト30台で、グリップは上級と初級に10台づつ別れたのですが、初めてのコース+初走行(車もFFもグリップも)という事で初級でスタート
大体30秒切るかが初心者の壁という事で30秒を目標に走行・・・
1本目はコース覚えながら様子見で、、、あれ、以外に前が詰まる、、、終わってみると30秒中頃でした。

2本目はちょっと冷静になってとりあえずいけるとこまで行ってみる、、、29.9秒キター!

3本目はブレーキ、アクセルのメリハリ、アウトインアウトを意識して走行、、、29.7秒!

ここで予想外の展開、、、なんと主催側より上級と入れ替わって欲しいとな ΣΣ(゚Д゚;)
(この日は上級の平均タイムが29秒台でした)

4本目は (((( ;゚Д゚)))ガクガクブルブル しながらコースイン、とりあえず置いてかれないように必死だったせいかタイムは伸びず

5本目は以外にまざっても迷惑にならない事がわかったんで、自分のペースで突っ込んでいく、、、29.5秒!!

6本目、コースにもなれてきてアンダーを出さないよう意識して、この日トップだったインプレッサ(27秒台でした)に必死についていってみる (;´Д`)ハァハァ

結果、29.3秒までタイムをのばせました。
(7本目は雨だったのでライン取りの練習〜)

最終的にこの日は4位でした、ドノーマルでここまで走れるのがさすがTypeRですね :)
とりあえず29秒が一つの壁になりそうです、でもその前にメーター付けなきゃ、、、ブレーキから煙があがっていたのはビックリしました(笑

実に3年ぶりのサーキット、車を走らせるのは楽しいもんですね♪

2008年3月19日水曜日

WiiFitはじめました

名古屋に来てからというものめっきり運動不足で3年で+7kgとメタポ一直線だったわけですが、
さすがにまずいと思ってWiiFit買ってみました。
まぁ買わなくても運動は出来るのですが、、、ゲーム的に出来る方が自分には向いてると思ったので :)

で、今3日目なんですが以外に良いです。
ちゃんと汗かくぐらい動くし、腰を痛めにくい筋トレ方法がわかったりと楽しんでおります。
とりあえず目指せ−3kg :)

2008年3月11日火曜日

GrailsをWebSphereで

WebSphere 6.1でGrailsアプリを動かした時のトラブル〜

・WASから色んな所で404 NotFoundが帰ってくる
WASの問題、 6.1.0.3以降のパッチで設定出来るようになった「com.ibm.ws.webcontainer.invokefilterscompatibility 」を true にしてやります。
古いバージョンだと他にも問題がありそうだったので、とりあえず最新の 6.1.0.13 で動作確認しました。

その他 index.gsp が見れない問題等は http://grails.org/Deployment に書いてあります。

GrailsでHibernate+C3P0のpooling

Grailsでpoolingをする場合、grails-app/conf/DataSource.groovy の pooled = true にしてやる事で pooling されるようですが、
これはHibernateのbuilt-in poolingを使っている(?)気がします。

で、built-in poolingは
Using Hibernate built-in connection pool (not for production use!)
ということで、本番では推奨されていません。

ここでHibernateが対応しているサードパーティのpoolingの出番です。
C3P0とApache DBCPとProxoolが使えるらしいのですが、今回はC3P0を使います。

grails-app/conf/spring/resources.xml というファイルを作成し、以下のように設定します。


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass"><value>oracle.jdbc.OracleDriver</value></property>
<property name="jdbcUrl"><value>jdbc:oracle:thin:@127.0.0.1:1521:orcl</value></property>
<property name="user"><value>username</value></property>
<property name="password"><value>password</value></property>
</bean>
</beans>


driverClassとjdbcUrlはDBに合わせて調整して下さい、今回はOracleへ接続しています。
resources.xmlを記述する事で DataSource.groovy の driverClass などは無視されます。

ここで一つ注意事項です。
C3P0では use_second_level_cache と use_query_cache を true にしてやる必要があります。

DataSource.groovy に以下を追加します。


hibernate {
cache.use_second_level_cache=true
cache.use_query_cache=true
cache.provider_class='org.hibernate.cache.EhCacheProvider'
}


追加しなかったり、falseにしても普通に動作してしまったりするのですが、環境によりエラーが出るので true にしておくのが良いみたいです。

2008年3月3日月曜日

OracleとCLOBとGrailsと

OracleのCLOB型はデフォルトでDISTINCT出来なくなっています(パフォーマンスの問題?)
なのでGrailsからクエリを直接実行しようとした場合は注意が必要です。
DISTINCTしなければいいのかというと、そういうわけでもなく、 like は使えるが = は使えないという非常に微妙な状態になります。(Oracle8ではlikeもNGっぽいです)
以下にテストした結果、OKなケースとNGなケースをまとめます。

以下の形のドメインクラス

class Book {
Author author
String title
}


class Author {
static mapping = {
clob type:'text'
}

String clob
}


以下のクエリを実行します。

SELECT DISTINCT a from Author as a // NG

from Author as a WHERE a.clob like '%Clob%' // OK

from Author as a WHERE a.clob = '%Clob%' // NG

SELECT DISTINCT b from Book as b WHERE b.author.clob like '%Clob%' // OK

SELECT DISTINCT b from Book as b WHERE b.author.clob = 'Clob' // NG


Oracleが嫌いになりました :p

2008年2月27日水曜日

GrailsでOracle、CLOB型

OracleではString型はVARCHAR2の4000文字が最大となります。
これ以上長い文字列をいれようと思った場合はCLOB(わけのわからん互換性の低いいやーな感じの型)などを使うのですが、これはStringではないのでStringと同じようには動作しません。
(likeや一致などでエラーになったり)

で、Grails上で使う場合ですが、CLOB型のフィールドを作るにはドメインのmappingに指定してやれば作る事が出来ます。


class Book {
static mapping = {
text type:'clob'
}

String text
}


はい、ですが、上にも書いたとおりCLOB型はStringではないので、検索などを実行するとエラーになります。
CLOBとStringをキャストしてやる必要があるのですが、ここら辺のDBの非互換性を制御するためにあるのがHibernateのDialectというやつです。
GrailsでDialectを設定するには conf/DataSource.groovy を編集します。
dataSourceの部分に dialect を追加してやります。


dataSource {
pooled = false
dialect = org.hibernate.dialect.Oracle9Dialect.class
driverClassName = "oracle.jdbc.OracleDriver"
username = "user"
password = "pass"
}


[2008.03.03] 追記 ---------------------------------------------------
dialectは指定しなくてもいけるみたいです。
クエリ自体は最適化されてるっぽいのでDBを固定出来る場合は書いておいた方がいいかも
-----------------------------------------------------------------

これで検索(Book.findAllByTextLike("妄想") 等)を実行してみると、、、おおっと!エラー
Dialectで方言を吸収してくれるのですが、CLOBの宣言のままだとHibernateは「CLOBとして使いたいんだろうなーこいつ」と判断してくれます。
なので宣言を text にします。


class Book {
static mapping = {
text type:'text'
}

String text
}


これでHibernateは「あー、Oracleで長文使いたいんだねー、じゃあDBの型はCLOBにしとくけどStringとして使えるようにしとくよー」と脳内会話が繰り広げられ無事解決です。

また、このFieldTypeの変換部分は、org.hibernate.types.UserType を継承したUserTypeを作成し、mapping の type にクラス名を指定する事で自分で作成する事も出来るようです。試してませんが :)

2008年2月22日金曜日

groovyでPDF! PDFBuilder!

groovy-pdfというおもしろそうなものを見つけたのでgrailsで使ってみました。
一言で言うと、iTextをgroovyのBuilderで実装しましたよってものです。

まずはインストール。
ソースをgoogle codeから落としてきましょう。

svn checkout http://groovy-pdf.googlecode.com/svn/trunk/ groovy-pdf

ソースしか上がってないので自分でコンパイルしてやる必要があります。
groovyをインストールし、gantをダウンロードしインストール。
(解凍したフォルダに移動して、 groovy bin/install.groovy)
コンソールからgantで実行し、Cannot open file build.gant とメッセーじが出たらインストールされています。

先程チェックアウトしたプロジェクトのフォルダへ移動し、gant compile と実行すれば dist/pdfbuilder.jar というファイルが出来るので、それを grailsプロジェクトのlibフォルダへコピー
あとはサンプルの内容をアクション内にコピペしてやればとりあえずテキストが出てきます。

まだ対応していない処理などもありますが、Builder形式でかけるのでBuilderの書式を覚えてしまえば誰でも簡単P・D・F!P・D・F!

デフォでは日本語がでませんので、この辺はiTextの設定をまた後日 :)

2008年2月13日水曜日

納車


先日待ちに待ったシビックTypeRが納車されました。
丸2ヶ月ぐらいかかった分もあってか非常に快適です :)

実に5年ぶりのスポーツカー&初のVTECです。
ストレートのフル加速はさすがにFDやエボには劣りますが、レスポンスのダイレクト感とVTECに入った後の間隔はやばいぐらい気持ちいいです。
公道ではまともにVTECを使えないので早くサーキットで走ってみたいです :)

・画像を追加、どこからアップロードするかなかなか気づかなかったのは秘密

2008年2月12日火曜日

GrailsでのClass.forNameについて

grails 1.0以前では自前で書いたクラスを Class.forName("className") で呼び出せてたんですが、今 grails 1.0.1-SNAPSHOT でやってみたら ClassNotFound のエラーが、、、(´・ω・`)

で、Javaのリファレンスを見てみると

Class.forName("Foo")

Class.forName("Foo", true, this.getClass().getClassLoader())
を呼んでるのとの事

なんでクラスローダーのへんの問題かと思い、とりあえずそのまま実行してみると、、、さっくり動いちゃいました。
ということは groovy でオーバーライドされちゃってるんでしょうか、まぁこんな事もありましたというメモメモ。

2008年2月6日水曜日

TomcatでJNDI in Grails

grailsから作成したwarをtomcat上でJNDIを使って動かすというのをやったので覚え書き。

・grailsからwarの作成

grails war
以上 :)
{applicationName}-0.1.war って感じのファイルが出来れば成功です。

・grailsのDataSource.groovyをJNDIへ書き換える

warした場合は production を書き換えます。

production {
  dataSource {
   pooled = true
   dbCreate = "update"
   jndiName = "java:comp/env/jdbc/oracle"
  }
}


urlがなくなってjndiNameに変わります。
java:comp/env/ までは固定で、 jdbc/oracle というのがJNDIの接続名になります。

・Tomcat側の設定

コンテキストにたいしてJNDIを設定します。

tomcat/conf/server.xml の中の <Host ...> の中に以下の内容を追加します。


<Context path="/hoge" docBase="hoge"
debug="5" reloadable="true" crossContext="true">

<Resource name="jdbc/oracle"
auth="Container"
type="javax.sql.DataSource"
username="user"
password="pass"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@127.0.0.1:1521:hogedb"
/>

</Context>


ContextのpathとdocBaseにはプロジェクト名を、
ResourceのnameにJNDIの接続名を、
username,password,driverClassName,urlはDBに応じて設定して下さい。
必要なドライバを tomcat/common/lib にいれておきます。

・起動

作成した war を tomcat/webapps/ へいれ、tomcatを起動します。
エラー無くたちあがれば成功です。
Tomcat上のJNDIの状態を知りたい場合は、TomcatAdministratorを入れれば簡単に確認する事が出来ます。


・おまけ

上記の設定だとコンテキスト毎にJNDIの設定を書く必要がありますが、複数のコンテキストで共通のJNDIを使う場合は以下のように設定します。

tomcat/conf/server.xml 内の <GlobalNamingResources> の中に <Resource> を設定します。(内容は同じです)
Context内に <ResourceLink> を設定します。


<Context path="/hoge" docBase="hoge"
debug="5" reloadable="true" crossContext="true">

<ResourceLink name="jdbc/oracle"
global="jdbc/oracle"
type="javax.sql.DataSource"/>

</Context>


これでGlobalNamingResourcesに設定したResourceを設定できます。

2008年1月22日火曜日

Ext-JS 2.0 速度検証(フォームapplyTo,transform編)

前回のサンプルに近い適当なフォームを用意して 100個作成と applyTo と transform で作ってみました。

ext-base
11712ms

・・・で、FireFoxがハングします(´・ω・`)
早くもないしちょっぴりショック、、、

で、気をとりなおして Safari でも計測

Safari
1306ms

            スポポポポポポーン!!!
      。     。
        。  。 。 。 ゚
       。  。゚。゜。 ゚。 。
      /  // / /
     ( Д ) Д)Д))


Safariはやいなー、JSの実行もエレメントの追加/削除もFirefoxとは比べ物にならない早さ・・・
ただComboBoxがうまくtransformできませんでした(´・ω・`)
(セレクタの位置が変な所にいってしまいます)

うーん、JavaScriptでダイナミックにフォームを作るのはもう少し様子見で・・・

Ext-JS 2.0 速度検証(FormPanel)

Javascriptのコンパイル(圧縮)で計測に使ったJSの重い処理ですが、
Ext-JS1.1を使ってダイナミックにフォームを作ってます。

Ext-JS2.0では早くなったのか検証してみました。(同じ処理をしているわけでないので比較にはならないですが、、、)

まず公式サンプルのフォームを使ってテストしてみます。
リンク先の Form 1 - Very Simpleです。

このフォームを100個作成してかかった時間をはかっています。(FireFoxで行いました)

ext-base
10488ms
10768ms
11003ms
10428ms
10556ms

jQuery 1.1.1
10824ms
10961ms
10877ms
11029ms
11118ms

yui-utilities 2.2.0
10778ms
10793ms
10547ms
10954ms
10502ms

prototype 1.5.0
10840ms
10502ms
10507ms
10852ms
10898ms

変わらんですね〜、んでは$()が300%早くなったという噂のjQuery1.2.2を使ってみましょう、、、

jQuery 1.2.2
10856ms
11147ms
11139ms
11134ms
11140ms

・・・(#^ω^)ピキピキ

せっかくなんでやたらと快適な Safari3 でも試してみました。

safari(ext-base)
5874ms
5845ms
5885ms
5902ms
5881ms

  。 。
 / / ポーン!
( Д )

・・・えーっと、ってことはブラウザのエレメントの追加/削除が単純に重くてJavascriptが悪いって事ではないのかな
(Firefox最近やたらと重いんだよなぁ、、、拡張もたいしていれてないのに、微妙になってきたなぁ)

次回は、dom操作が少ないと思われる、既存のフォームからext-formへapplyしていった場合の速度を計ってみたいと思います。

2008年1月20日日曜日

引っ越し準備

プロフィールに名古屋とか書いちゃってますが、実はまだ名古屋んではなかったりします・・・
@1Weekで引っ越しなんで準備でてんわやんわ(古い?)です

引っ越しの度に思うのですが、びっくりするほどゴミが出ます。
今の所にひっこしてから一切手つかずだった燃えないゴミをひっぱりだしたらなんと、、、


iPodが発掘されました



ワロス

2008年1月16日水曜日

MacでGrails! 超入門編(Hello World!)

まずはお決まりのHelloWorldです。
コマンドプロンプトより、以下のコマンドでプロジェクトを作成します。

grails create-app hello

helloがプロジェクト名になります。
だらだら〜っとメッセージが流れてプロジェクトの雛形が完成です。

作成したプロジェクトのフォルダへ移動し、コントローラーを作成します。

cd hello
grails create-controller home

以下のファイルが作成されます。
hello/grails-app/controllers/HomeController.groovy

コントローラーとはアクションの集まりです。
今回はHelloWorldを画面に表示する、というアクションを作ります。

HomeController.groovy を編集します。


class HomeController {
 def index = { }
 def world = { render "Hello World!" }
}


これで HomeController には index と world というアクションが作成されました。
では以下のコマンドを実行して起動してみましょう。

grails run-app

つらつら〜っとメッセージが出てきて、以下のメッセージが出た所で起動完了です。

Server running. Browse to http://localhost:8080/hello

http://localhost:8080/hello へアクセスしてみましょー
Welcome to Grailsのメッセージと共に HomeController へのリンクが表示されていれば完璧です :)

早速 HomeController へのリンクを踏んでみましょう・・・エラーが出ますね ^^
404エラーが出ています、が、これは正しい動きです。

現在のURLを見て下さい、 http://localhost:8080/hello/home になっていると思います。
これは「hello プロジェクトの home コントローラーの index アクションを実行」という動きになります。
index アクションは何も作ってないのでエラーになってしまった、というわけです。

では先程作った world アクションを実行してみましょう。
感のいい方ならもうわかったかもしれませんが、worldアクションの実行は http://localhost:8080/hello/home/world へアクセスすると実行出来ます

URLとコントローラー、アクションの関係は以下のとうりです。
http://localhost:8080/{projectName}/{controllerName}/{actionName}
index はちょっと特殊で、アクション名をつけなかった場合は index が動作します。
(もちろん、 http://localhost:8080/hello/home/index としても動きます。)

Hello World!が画面に表示されましたか?
表示されていればもうあなたはgrailsユーザーです ^^

2008年1月15日火曜日

MacでGrails! 超入門編(概要とセットアップ)

最近メインで使っているフレームワーク、Grailsを紹介していきます。
まだまだ日本では知名度は低いですが、将来有望(?)なフレームワークです。
わかりやすく乱暴にいってしまうと RubyOnRails の Java になったバージョンです。(RoRと目指す所は違います)

利点
・Javaの様々な資産を利用出来る
・コアはSpringFrameWork、Hibernate等定評のあるソフトで動いている
・Groovy言語を使用し、Javaのめんどうなソースを簡略化可能
・Java(ほぼ99%ぐらい)そのままで書く事も可能

欠点
・発展途上の為仕様が変わる事が良くある
・リリースされたバージョンといえども一部バグがあったりする
・ちょっと重い?

まぁ欠点はいずれも時間が解決していく系だと思います。

で、Macで動かしてみましょー
といっても開発陣はMacが多いようでMacで動かすのはとても簡単です。
Winでもほぼ同じです、試した事はないですがWinではインストーラーもあるようです :)

http://docs.codehaus.org/display/GRAILS/Japanese+Installation
ここの「ダウンロードしてインストール」に従って進めていきます。

ここで日本語対応の為のおまじないをかけておきます。
grailsを解凍したフォルダの bin/startGrails の先頭の方(10行目辺り)へ以下の行を追加します。

JAVA_OPTS="-server -Dfile.encoding=utf-8"

これでファイルをUTF-8で作成すれば日本語はOKです。
コマンドプロンプトから「grails」を実行してメッセージが表示されたら次のステップです!

2008年1月8日火曜日

Cubby セットアップ編

ふと Cubby をインストールしてみた。

公式のセットアップ(http://cubby.sandbox.seasar.org/setup.html)に従い進めていく。

Mavenをインストール、よくわからないままパスを通してコマンド実行
ごりごりとダウンロードがはじまり、しばらくして終了

mvn tomcat:run してとりあえず起動、またまたダウンロードがはじまる、、、
止まった所で http://localhost:8080/artefactId へアクセス、おー動いてる!

サンプルのおうむ返しをチュートリアルを読みながら解読、基本は今流行のRails系な感じ
まだやってないけどEclipseのプロジェクトもMavenからサクっと作れるみたいなので、
保管とか楽そうでいいなー、Javaっぽいめんどくささはアノテーションで頑張っているみたい

ここまでダウンロード時間も含めて10分ぐらい、Seasar自体まったく触った事なかったんですが、これなら入りやすいですね。
とりあえず次回はもう少し触ってみたいと思います

Javascriptの圧縮(gzip編)

まずは容量比較から

通常状態(788kb)
yuicompressor(718kb)
yuicompressor+gzip(196kb)

おー、だいぶ変わりましたね。

続いて読込み/スクリプト実行速度の計測です。

1770ms / 3856ms
1790ms / 3798ms
1690ms / 3797ms
1820ms / 3738ms
1620ms / 3793ms

ローカルだと違いが出ませんでしたが、容量はあきらかにへっているので、実運用では有効に働きそうですね。
ただローエンドなクライアントでgzipの処理がどの程度負荷になるのか気になるところです。

2008年1月7日月曜日

Javascriptのコンパイル(圧縮)

Javascriptの圧縮をYUI Compressorでテストしてみました。
圧縮によるクライアントへの受信速度と、圧縮されたスクリプトの実行速度の計測です。

対象となるのは、サイズばらばらのJavascript16ファイル(全788kb)を表示、
JavaScriptにて約1000のエレメントを追加するページ。

無圧縮(788kb)でのロード時間 / スクリプト実行時間
1530ms / 3776ms
2360ms / 3751ms
2650ms / 3789ms
2290ms / 3754ms
1500ms / 3789ms

圧縮時(718kb)のロード時間 / スクリプト実行時間
2480ms / 3818ms
1980ms / 3766ms
2520ms / 3788ms
1480ms / 3761ms
2470ms / 3713ms

か、かわんねぇ〜・・・ローカルで動かしてるから通信速度が遅いほど効果はあると思います。
とりあえず圧縮してもスクリプト自体の実行速度は同じ、と(当たり前か)
(容量があんまりかわってないのは元々圧縮されたものが多いため)

というわけで次回は+gzipで計測してみます。

2008年1月4日金曜日

思い立ったが吉日

誰に見せるわけでもないけど、やれる事はやっておこうという精神で。

で、Adobe Airの話です。
前から気になっていたのですが、なかなか触る時間もなく放置だったAirをインスコ。
適当に調べてとりあえずHelloWorld! > オウム返しの作成

Hello.xml

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.0.M6" version="1.0">
<filename>Hello</filename>
<id>com.kujirahand.test.Hello</id>
<initialWindow>
<content>Hello.html</content>
<visible>true</visible>
<width>640</width>
<height>480</height>
</initialWindow>
</application>


Hello.html

<html>
<head>

<title>Hello</title>
<script type="text/javascript"><!--
function say() {
alert("Hi "+document.getElementById("name").value);
}
//-->
</script>
</head>
<body>
Input your name:<input type="text" name="name" id="name">
<input type="button" onclick="say()" value="OK"/>
</body>
</html>


何事も無く順調に、、、ってあれ、日本語入力できなくね?
ちょっと調べてみるとまだ日本語入力には対応してないっぽい、、、しょぼーん
というわけでしばらく様子見なAirでした。