2011年8月22日月曜日

JavaScript-GWTでのメッセージング

JavaScript-GWTでのメッセージング


概要

GWTで記述された非同期(同期は制限付きで可能)なメッセージング機構ライブラリ

MessengerGWT の紹介。



構造は単純で、

・HTML5(一応)規格内に含まれている、Window.postMessage を使用。

・GWTMessengerImplementとGWTMessengerInterface のクラスで構成

・相手先の名前と、コマンド(Stringによる識別子)でもって、オブジェクト間でのメッセージングが可能。



DL&ScreenCast

DL:

https://gitorious.org/messengersystem-gwt/


ScreenCast:

    インストール

http://www.screenr.com/Dsgs

    応用

http://www.screenr.com/LSgs



コード

送付側:

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月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 
        (発音はグウィット 2009年ころにIOで突如言い出してた気がする。)

    Googleが作ったJava言語を利用したJSコンパイル技術。FWではない。
    Java to JSの変換技術。
     ToJSCompiler(てか正確にはCompile to JS かな。)

    ・Javaで記述、コンパイル結果がJavaScript(JS)として実装、実働

    ・Javaのメソッドやクラスが使える

    ・Eclipseなどと連携可能
        ステップ実行ができる、プラグインが使える、などなど
        鬼便利というかこれが肝コレだけコレが全て

    ・JUnitが使える、テストが可能

    ・JavaScript単体では書けないような範囲の物事を、
        Javaの型安全な世界で記述する事ができる

    ・JSのローディングやテストを、Javaに関連する世界で自動化できる

    ・あらかじめ用意されているライブラリがたくさん
        イベントリスナ、JSON、JSONP、
        GUI Widget(ダサい。Apple並みとは言わないまでも、、)、
        Canvas、Sound、他

    ・JSも使える Java中JS、JS中Javaが可能
        JSNI(JavaScriptNativeInterface)


落とし穴もがっちりある
    JavaかとおもったらJSだった。何を言っているかわからねーと思うが(ry

    ・変換不可能なクラスもある
        日付に関するクラスとかが無い。めんどかったんだと思うが。

    ・Widgetが破滅的にダサい
        グラフィカルな面で本当に残念です。ありがとうございます。

    ・ロードとstaticの関係
        staticなどを使うと、優先的にLoadingが組まれる。結果として、
        名前空間が効かないポイント(=衝突を考慮する必要がある部分)が生まれたりする。
        =PureJSと同じ駄目さ

        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)

        →    1D後、1W後、1Y後に見て、何処が「足された部分」か、自分や他人にわかりますか? ッつー話。
    

    ・トラブルを起こさないように実装するには、階層を分けてのFW化(e.g. RoR)、ルール化が有用だが、。
        ルールが増える事(ルール爆発)は自殺行為→多彩なルールを守る事にコストがかかってしまう。
        しかもそれは永遠に続く。


    →解決策として、コンパイル という「改変」行程が選ばれる。



JSをコンパイルの結果として作りあげる技術たち ToJSCompiler

    Google Closure Tools (JS to JS コンパイル)
        http://code.google.com/closure/
        強くルール付けされた構造を模倣する事でのプラグイン化
        自己制約をパッケージ化する

    CoffeeScript (Coffee to JS コンパイル)
        http://jashkenas.github.com/coffee-script/
        JSの穴をあらかじめ殺した記法での記述と、コンパイル処理による最適化、ネームスペースの補助
        RoR3系で色々動きがあるらしいが?よく知らない。
        これからJSをどうしても書かなければ行けない分野では来ると思う。

    GWT (Java to JavaScript コンパイル)
        http://code.google.com/webtoolkit/
        Googleが用意した特定のクラス、メソッドがJSに変換される。
        コンパイル処理による最適化、パッケージによるネームスペース明示、
        staticによるシングルトン明示などができる。
        おまけでブラウザ間の差異を勝手に吸収する。
        Java言語自体に素敵さが足りないので、後述のJSCompilerたちの方が先があると思う。

    Pyjamas (Python to JS コンパイル)
        http://pyjs.org/
        触った事無いからわからん

    Js_of_ocaml
        触った事無いからわからん

    ClojureScript (Clojure to JS コンパイル)
        https://github.com/clojure/clojurescript
        さわり中 まだよくわからん

    ScalaJS (Scala to JS コンパイル ? 未登場)
        まだ無いのでわからん。作ってるらしー。 自分的に本命。
        現在のactorの挙動とか、JSフレンドリーなんじゃないかなーと思ったり。
        メソッドを渡す方法が効率的に記述できるようなバックドアが着いたら、
        ぼくのかんがえたさいきょうのげんご に近づく。
        理由は参照コピーと同義だから。


A to JavaScript の利点
    とあるA言語の能力 >>>>>> JSの能力 である前提がある。

    Aという言語があり、ルール的にJavaScriptよりも厳格 かつ、機能が定義されているとする。
    名前空間、記法制限、型、演算子、メッセージング、クラス、構造体、列挙子、、、

    これらを、その言語で書き、JSにコンパイルする。

    元々のAという言語にある機能を、Aという言語の記法で使う範囲でのみ担保すればいい。
    →最終的に出力されるJSには、手を触れる事が無い(あるとしたらA to JSコンパイラのバグとか不備)


    例えばクラスをJSで実装する場合、
    JSで直に実装→JSを書く際に、その自己定義した構造にあわせたreadとwriteが必要になる
    →ルールを言語の内側につくるので、さらに内側に書くしかない。

    他言語で書く場合
    →ルールは記述時言語側が持っているため、書き手は意識しないでいい。
    →コンパイルされた結果は、ルールが構築されたJSの中に、手続きが記述された状態になる。
    →結果は同じであっても、人が煩わされる行程が減っている、自動化できている事に注目。


ToJSCompilerとしてのGWT
    締めに、JSCompilerとしてのGWTの特性を上げる。

    ・Java記法で書ける
    ・パッケージでの名前空間の保護ができる
    ・JSNIで、JavaとJSがつなげる。JSNIは先行変換される部分記述のようなもの。
    ・最終的には結局JSに変換される

    特筆すべきは「結局JS」という部分。
        JavaとJSのハイブリッドの意味や価値がここにある。

    Pure Javaではできなかった、
    メソッドのオブジェクト化などがJava記法とJS記法の組み合わせでできる。

        private native JavaScriptObject get () /*-{

        return @com.kissaki.client.MessengerGWTCore.(略)::mtd(Ljava/lang/String;); 

         }-*/;



        これはどうよ。凄くないか。


        mtdメソッド
        public void mtd(String str) メソッドを、JavaScriptのオブジェクトに変えている。
        バリューとJavaのメソッドを関連づけたり、メソッド自体を疑似ポインタとしてぶん投げることができる。
        
        おそらく他の言語でも、JSでしかできない事を担保するために
        従来の言語の記法を拡張してブリッジにするような目論みとして、
        GWTにおけるJSNIみたいなものが出てくるはず。


        
今の俺の結論
     ToJSCompilerオハコン。
    言語の中に言語で機能を追加する副作用を回避できる。
    ブラウザで動く言語がJSに限られている事が一因だが、今後は異なるといいなーとか。JSのNativeライブラリ化とか進むといいなー。

(追記)タイトルをToJSCompilerに変えた。JSCompilerだとJSをコンパイルするように見える、と指摘受けた。11/08/23 11:40:33

フォロワー