ActionScriptでWebサーバーと分散オブジェクトを書こうとしてみた

そして挫折した話。


Flexでターミナルエミュレータ & TelentFlexを勉強中なのですが、「新しい言語を学ぼうと思ったら、その言語でWebサーバーを書いてみるといい」と、どこかで聞いたことがあるような気がしたので、ActionScriptでWebサーバーを書いてみようと思ったわけです。


サーバーもActionScriptだったら、FlashRemotingゲートウェイだとかAMFの型のマッピングがどうこうだとか何も考えなくても、ByteArray#{read,write}Objectを使ってオブジェクトやメソッド呼び出しを直接投げ合えるんじゃないか。これはすごい!


毎回writeObject(hogehoge)...と書くのは野暮なので、dRubyJava RMIのように、プロキシオブジェクトを通して見た目上直接メソッドを呼べたら嬉しい。↓こんな感じ。

var obj:RemoteReference = RemoteReference.conect("example.com", 8080);
var retval:* = obj.hogeMethod();  // サーバー側でhogeMethodが実行されて、返り値が返ってくる!


RemoteReferenceクラスに想定されるメソッドを全部定義しておくのはやってられないので、定義されていないメソッドが呼ばれたときにアクションを起こすようにしたい。Rubyにはmethod_missingメソッドがあるけど、ActionScriptでmethod_missing相当のことができないかなーと探してみると、あった。flash.utils.Proxyを使って作れる。 method_missing in ActionScript 3/Flex

package
{
        import flash.events.*;
        import flash.utils.*;
        import flash.net.Socket;

        public dynamic class RemoteReference extends Proxy
        {
                private var socket:Socket;

                public static function connect(server:String, port:int):RemoteReference
                {
                        var socket:Socket = new Socket();
                        socket.connect(server, port);
                        return new RemoteReference(socket);
                }

                public function RemoteReference(connected_socket:Socket)
                {
                        socket = connected_socket;
                }

                flash_proxy override function callProperty(method:*, ...args):* {
                        try {
                                var klass:Class = getDefinitionByName(getQualifiedClassName(this)) as Class;
                                return klass.prototype[method].apply(method, args);
                        }
                        catch (e:Error) {
                                // ありもしないメソッドが呼ばれた
                                return methodMissing (method, args);
                        }

                }

                protected function methodMissing(method:*, args:Array):* {
                        socket.writeObject(method);   // メソッド名を送る
                        socket.writeObject(args);     // 引数を送る
                        var retval:* = socket.readObject();     // 返り値を受け取る? EOFError!
                        var exception:* = socket.readObject();  // 例外を受け取る? EOFError!
                        if(exception) { throw exception; }
                        return new RemoteReference(socket, retval);
                }
        }
}


これを使ってみると、確かにmethodMissing()メソッドが呼ばれるのだけど、readObjectの時にEOFErrorが発生してしまう。システムコールのread()とかとは違って、Socketクラスのread系メソッドはデータが到着するまでブロックしたりはしないらしい。そこはActionScript流にシングルスレッド & イベントドリブンで書き続けないといけなくて、socketにProgressEvent.SOCKET_DATAイベントが届いたときに返り値を受け取らないといけない。


となると、サーバー上のメソッドを呼び出した後、すぐには返り値を受け取れない。と言うことは…

var obj:RemoteReference = new RemoteReference.conect("example.com", 8080);
trace( obj.hogeMethod() );   // まだ返り値は届いていない!

↑こういうコードは書けなくて、↓こんな風になるはず。

var obj:RemoteReference = new RemoteReference.conect("example.com", 8080);
obj.hogeMethod().addEventListener(RemoteReference.RETURN, hogeMethodHandler);  // 返り値が届いたらhogeMethodHandlerが呼ばれる


うーむ。結構大変かも。

そういえば、ActionScriptのSocketはListenできない!そこはRubyか何かで中継サーバーを書けばいいとしても、複数のクライアントを待ち受けようとしたら…中継サーバーとのプロトコルは…
というわけで挫折して今に至る。


ネタとしては面白そうですが、どなたかやりませんか (^_^;)