JavaScript-GWTでのメッセージング
概要
GWTで記述された非同期(同期は制限付きで可能)なメッセージング機構ライブラリ
MessengerGWT の紹介。
構造は単純で、
・HTML5(一応)規格内に含まれている、Window.postMessage を使用。
・GWTMessengerImplementとGWTMessengerInterface のクラスで構成
・相手先の名前と、コマンド(Stringによる識別子)でもって、オブジェクト間でのメッセージングが可能。
DL&ScreenCast
DL:
https://gitorious.org/messengersystem-gwt/
ScreenCast:
インストール
応用
コード
送付側:
messenger = new MessengerGWTImplement("master", this);
messenger.call("anotherObject", "command", messenger.tagValue("タグ", "値"));
受付側:
@Override
public void receiveCenter(String message) {
String exec = messenger.getCommand(message);
JSONObject tagValue = messenger.getJSONObjetFromMessage(message);
}
これだけで、"master" から "anotherObject"へ、メッセージ送信と受信ができる。
この例は非同期。callメソッドの代わりにsCallを使うと同期になる。
使用条件
・InterfaceとしてGWTMessengerInterfaceをimplementsする
implements MessengerGWTInterface
・GWTMessengerInterfaceで定義されているメソッドを@Overrideする。
public void receiveCenter(String message) {
// TODO Auto-generated method stub
}
・MessengerGWTImplement クラスのインスタンスを持つ。
MessengerGWTImplement messenger;
使用のための概略
メッセージングに「参加」するオブジェクトは、すべからく
MessengerGWTInterface をimplementsしており、
MessengerGWTImplementクラスのインスタンス messenger を持っている
という前提で動かす。
messengerインスタンスから送り先を指定して、
messagingを送る(call)事で、
もし対象が存在していれば、メッセージが対象の receiveCenter メソッドに届く。
仕組み
送信に、Window.postMessage APIを使用している。
GWTでJSON形式のコンテナを用意し、
相手名、コマンド(メッセージにつける名前)、その他の情報などを
Stringとして渡し、受け取ってからJSON化している。
そのため、値の受け渡しがメインになっており、値の参照などは受け渡せない。
JavaメソッドをJSObject化して送る、などができるのかもしれないが、
Stringからメソッドに変えての実行は嫌な予感がするのでおすすめしない。
極力GWT独自の仕組みを使わない事で、今後他のJSCompilerが出てきても、
対応できるように(あるいは共存できるように!)する目論み。
工夫ポイント
メッセージの送受信が可能な条件として、親子関係が確立されている、という条件を持たせている。
オブジェクトA Bがあったとき、オブジェクトAからBへとメッセージを送る/受け取るには、
A、Bの間に 親子関係 がある必要がある。
A.messener.inputParent("Bのmessengerの名前"); か、
B.messener.inputParent("Aのmessengerの名前");
こうすると、
A/Bから他方へとinputParent という”自分、君の子ですよ”コールが行き、
条件を満たせば子供に加わる事ができる。
こうする事で、メッセージを送る側、受ける側の関係に最低でも親子関係というコードが必要になり、
親子関係部分のコードを見れば誰と通信する可能性があるのか限定できる。
この機構はObjective-C版のMessengerSystemと同様。
https://gitorious.org/messengersystem-obj-c
で、親子関係があるので、子から親の呼び出しには
messenger.callParent(command, tagValue);
を使い、メッセージ送付先の名前を省く事ができる。
自分自身は、
messenger.callMyself(command, tagValue);
で呼べる。
搬送物について
tagValue には、色々なものを詰められる。
messenger.callMyself("command",
messenger.tagValue("tagString", "String"), //文字列
messenger.tagValue("tagDouble", 100.0), //Double
messenger.tagValue("tagInt", 100), //Int
messenger.tagValue("tagJSONArray", new JSONArray()), //JSONArray
messenger.tagValue("tagJSONObject", new JSONObject()), //JSONObject
messenger.tagValue("tagJSONObject[]", JSONObjects...) //JSONObject[]
);
などなど。で、しれっと書いたけれど、
tagValue パラメータは可変長のJSONObjectになっているので、複数のオブジェクトをMap的に
messenger.callMyself(command,
messenger.tagValue("tag", "value"),
messenger.tagValue("tag2", "value2")
);
入れることが可能。
また、省略して
messenger.callMyself(command);
と書くことも可能。
受ける側は、
String value = messenger.getValueForTag("tag", message);
でメッセージに含まれるTagValueを取得できる。
ただ、パースしてるだけなので、一個ずつ要素をパースするのはかなりパフォーマンス案配が悪い。
なので、
JSONObject rootObject = messenger.getJSONObjetFromMessage(message);
で、JSONObjectにしてから、独自に rootObject.get("タグ名"); などが使用可能。
その場合は自身で、Stringなら
String value = rootObject.get("タグ名").isString().stringValue();
などの処理が必要。
既存の実装上の欠点
・受け側の仕組みにGWTのイベントを使用している箇所がある
なんとかして無くしたかったが、GWTからはWindow.postMessageがstaticメソッドでないと
メソッドのロード順の問題を突破できず機能しないため、苦肉の策。
GWT以外であれば、受け側の仕組みを変えるだけで移植可能なはず。
・値の出し元と受け取り先に、参照ポインタ的な接続が無い
これは、値を受け渡す際に、文字列を飛ばしているだけなので、
その分のメモリをもれなくがっつり、コピーしているのと同じコストを払っていることを意味する。
Objective-Cであればポインタ渡しにできる所を、値をコピーして渡す事しかできない。
なので、あんまりでっかいものを送付すると、まんまコピーになるので、その後GCが大忙しになる。
このあたりは、methodを送る、という事が荒技でできるので、
そのあたりを利用すればいいかもしれない。
メッセージでメソッド自体をinvoker文字列として送りつけ、
相手側でinvokeメソッド化して値ゲット、という荒技。
試した事無い。が、できる、、んじゃないかなーうん。
・早さがまちまち
最速で0.01秒程度で送受信が完了する。ブラウザのメモリ依存のようだが、1sかかった事もある。
未達ケースは未体験。(まあロストしたら使ってないのだけれど)
・大体のIEで動かない
デスクトップ用の対外のIEはモダンブラウザではない(ぉ ので、問題点かどうかは怪しいが、
Safari,Chrome,FireFox3x~で確認済み。IE9はWP7 mangoのみ確認。(モバイル対応が優先されてるため。)
より具体的に範囲を言うと、Google+でもWindow.postMessage APIを使っているため、
Google+が対応を表明しているブラウザでは動く。
・遅延実行(withDelay)、実行ロック(withLock)が無い
Obj-C版にはある機能である、遅延実行と実行ロックがない。
遅延実行は「~秒後にメッセージ送付」。
普通に積めると思う。面倒で書いていない。
実行ロックは、同じmessengerにロックで指定したcommandが送られた場合、
ロックを解除していき、全てのロックが解除されたタイミングで元のメッセージを発行する、というもの。
値のクロージャなどが(ただ単に面倒で)ネックになって、実装されていない。
・実ブラウザでないと動作しない(= テストがガチテストになる)
GWTの標準で用意されているHTMLUnitはWindow.postMessageメソッド非対応。
のため、テストに使えない。
オプションの設定などを駆使して、実機環境と同じ環境でテストを組む必要がある。
・全部非同期で書くとテストがきっつい
まあきっつい。通信と同じ内容になる。
・同期がインチキ
callメソッドなどには、sで始まるsync系のメソッドが併せて用意してある。
現在は残念な事に、GWTのイベントで記述されている。
yieldがJavaScriptにあるのはFFだけなので、それ以外のブラウザで動くような同期が書けない。
そもGWTもその辺知ってて、yieldがそもそもコンパイルされない。
JSNIの中ですら怒られるレベル。
処理Aの途中でmessagingと同時にyieldし、相手にmessageが届き、
その返信を受け取ってyieldから処理Aを再開、という流れがとりたい訳だが、
FFにしかないので頑張らない。
どうせFFはもう、、、
WebWorkerでできるかもしれない。が、うーん、、どうなんだ。。
以上
2011年8月22日月曜日
JavaScript-GWTでのメッセージング
2011年8月21日日曜日
MacでVirtualBox
MacでVirtualBox
概要
Macでも動かせる仮想環境構築ソフトVirtualBoxを使って、
Mac上にUbuntu環境を作成する。
DLしとくべきもの
VirtualBoxアプリケーション
http://www.virtualbox.org/
DownLoadsから、自分の環境にあった物を見つける。
UbuntuのisoイメージをDLしておく
http://www.ubuntu.com/business/get-ubuntu/download
コレ書いてる時は、 ubuntu-10.10-server-i386.iso を使った。
手順(構築編)
VirtualBoxを起動
Linuxって選んでおく。Ubuntuゥー。
で、初回起動が終わると、今度はisoを選択、あらかじめDLしておいたUbuntuを入れる。
インストールメディアを選択。
デフォルトでトンチンカンな場所指してるので、フォルダマークからUbuntuのisoを選択。
あとは、OSに映った内容の通りにあれこれすると完了。
手順(複製編)
作った環境は、VirtualBoxのマネージャー上で一覧できる。
で、ここでは、複製は出来ない。じゃあVM全体をコピレばいーんじゃん、って考えたけど、VirtualBoxはUUIDで各VMを管理してる。
どのへんまで深く、値が振ってあるか判らん。
調べたところで、CommandLineが使えるという事が判った。
VBoxManage clonehd <uuid>|<filename> <outputfile>
[--format VDI|VMDK|VHD|RAW|<other>]
[--variant Standard,Fixed,Split2G,Stream,ESX]
[--existing]
usage:
VBoxManage clonehd old.vdi new.vdi
赤字部分は、.vdiを指定。
肝心の、既存のVMのこれらのファイルは、
Applications/VirtualBox VMs 下にある。
で、実際に実行する例として、画面と違って申し訳ないが、
VBoxManage clonehd Ubuntu2/Ubuntu2.vdi Ubuntu3/Ubuntu3.vdi
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
Clone hard disk created in format 'VDI'. UUID: 580b2d92-8ef0-4aa2-a029-db3f2670e7ca
実行すると、コピーされ、しかもUUIDも振り直される。
以前はエラーがあったようで、難儀したらしいが、MacでVer4系のVirtulBoxでは、とくにエラーに出会わなかった。
最後に
VirtualBoxのマネージャー上にて、
新規 で新規作成 → 適当に進めて、ディスク選択時、
既存のハードディスクを使う、ということにする。
フォルダのアイコンを押し、
先ほど複製した物を選択、
ユーザー、パスワード、起動ディスクとかの設定も全て生きているので、コピペしたつもりで使える。
以上。
2011年8月19日金曜日
ToJSCompilerとしてのGWTについて
ToJSCompilerとしてのGWTについて
GoogleWebToolkit GWT
Googleが作ったJava言語を利用したJSコンパイル技術。FWではない。
ToJSCompiler(てか正確にはCompile to JS かな。)
・Javaで記述、コンパイル結果がJavaScript(JS)として実装、実働
ステップ実行ができる、プラグインが使える、などなど
・JSのローディングやテストを、Javaに関連する世界で自動化できる
イベントリスナ、JSON、JSONP、
Canvas、Sound、他
JSNI(JavaScriptNativeInterface)
落とし穴もがっちりある
JavaかとおもったらJSだった。何を言っているかわからねーと思うが(ry
・変換不可能なクラスもある
日付に関するクラスとかが無い。めんどかったんだと思うが。
・Widgetが破滅的にダサい
グラフィカルな面で本当に残念です。ありがとうございます。
・ロードとstaticの関係
staticなどを使うと、優先的にLoadingが組まれる。結果として、
Javaであればリフレクションなどの手段で明確にアクセスできるところ、
・イベントの解除などはJSの癖を引きずる
イベントへのアイデンティファイに対して、制約が多い。根源がJSのリスナなのでしょうがない。
具体的には、特定のオブジェクトのみリスナから外す、という事ができない。
オブジェクトが帰属するクラス単位でのリスナがごっそりと消える。
(リスナのアイデンティファイについて、クラス単位より小さな単位でのアイデンティファイができない)
・JSNIはJSなので何が起こってもおかしく無いし補完が効かない
オハコンの中のオワコン
ところでJavaScriptとはなんだったのか
GWTのようなToJSCompilerが生まれた経緯を推察してみる。
JSとは、
HashMap a ... b
b ( =a)
a (=b)
a(function b()) など。
何でも連鎖マップに入れられ、発火させられる言語
→他の言語でやっている様々な事が、ほぼ、JSを使って、JSの上で実装可能。
ここに闇がある
☆他の言語でやっている様々な事が、ほぼ、JSを使って、JSの上で実装可能。
→言語の中に、同じ言語を使って、機能や仕組みを実装する、という行為には、元来無理がある
だって元々その言語に無いのだもの。
・内部にむき出しの実装が増える事によって、トラブルが増える
「実装した機能」それ自体の実装に触る事ができてしまう
e.g. C++のスマートポインタ、Javaのメッセージング
JavaScriptの場合は、元来無いものが非常に多い。
名前空間(致命傷)、クラス、メッセージング、自己環境把握概念
・実装機能と自分が書き足したものとの区別がつくのは今この瞬間の書いている自分だけ
a = a+b; (before)
a = a+b+c; (after)
・トラブルを起こさないように実装するには、階層を分けてのFW化(e.g. RoR)、ルール化が有用だが、。
ルールが増える事(ルール爆発)は自殺行為→多彩なルールを守る事にコストがかかってしまう。
→解決策として、コンパイル という「改変」行程が選ばれる。
CoffeeScript (Coffee to JS コンパイル)
JSの穴をあらかじめ殺した記法での記述と、コンパイル処理による最適化、ネームスペースの補助
RoR3系で色々動きがあるらしいが?よく知らない。
GWT (Java to JavaScript コンパイル)
Googleが用意した特定のクラス、メソッドがJSに変換される。
コンパイル処理による最適化、パッケージによるネームスペース明示、
staticによるシングルトン明示などができる。
おまけでブラウザ間の差異を勝手に吸収する。
Java言語自体に素敵さが足りないので、後述のJSCompilerたちの方が先があると思う。
Pyjamas (Python to JS コンパイル)
ClojureScript (Clojure to JS コンパイル)
さわり中 まだよくわからん
ScalaJS (Scala to JS コンパイル ? 未登場)
まだ無いのでわからん。作ってるらしー。 自分的に本命。
A to JavaScript の利点
とあるA言語の能力 >>>>>> JSの能力 である前提がある。
Aという言語があり、ルール的にJavaScriptよりも厳格 かつ、機能が定義されているとする。
名前空間、記法制限、型、演算子、メッセージング、クラス、構造体、列挙子、、、
これらを、その言語で書き、JSにコンパイルする。
元々のAという言語にある機能を、Aという言語の記法で使う範囲でのみ担保すればいい。
→最終的に出力されるJSには、手を触れる事が無い(あるとしたらA to JSコンパイラのバグとか不備)
例えばクラスをJSで実装する場合、
JSで直に実装→JSを書く際に、その自己定義した構造にあわせたreadとwriteが必要になる
→ルールを言語の内側につくるので、さらに内側に書くしかない。
他言語で書く場合
→ルールは記述時言語側が持っているため、書き手は意識しないでいい。
→コンパイルされた結果は、ルールが構築されたJSの中に、手続きが記述された状態になる。
→結果は同じであっても、人が煩わされる行程が減っている、自動化できている事に注目。
return @com.kissaki.client.MessengerGWTCore.(略)::mtd(Ljava/lang/String;);
}-*/;
これはどうよ。凄くないか。
今の俺の結論
ToJSCompilerオハコン。
言語の中に言語で機能を追加する副作用を回避できる。
ブラウザで動く言語がJSに限られている事が一因だが、今後は異なるといいなーとか。JSのNativeライブラリ化とか進むといいなー。
(追記)タイトルをToJSCompilerに変えた。JSCompilerだとJSをコンパイルするように見える、と指摘受けた。11/08/23 11:40:33