2009年9月9日水曜日

Google App EngineでStreamingAMFChannelが使えたかとぬかった

昨日の記事に書いた以下の例外メッセージを見ていて、要はBaseStreamingHTTPEndpoint.javaの833行目にあるコード「currentThread.setName(threadName);」、つまりスレッドにsetNameしている部分をコメントしてみたら解決するのではないかということに。
2009/09/08 4:57:25 com.google.apphosting.utils.jetty.JettyLogger warn
警告: /messagebroker/streamingamf
java.security.AccessControlException: access denied (java.lang.RuntimePermission modifyThread)
 at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
 at java.security.AccessController.checkPermission(AccessController.java:546)
 at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
 at com.google.appengine.tools.development.DevAppServerFactory$CustomSecurityManager.checkPermission(DevAppServerFactory.java:139)
 at com.google.appengine.tools.development.DevAppServerFactory$CustomSecurityManager.checkAccess(DevAppServerFactory.java:178)
 at java.lang.Thread.checkAccess(Thread.java:1263)
 at java.lang.Thread.setName(Thread.java:1050)
 at flex.messaging.endpoints.BaseStreamingHTTPEndpoint.handleFlexClientStreamingOpenRequest(BaseStreamingHTTPEndpoint.java:833)
 at flex.messaging.endpoints.BaseStreamingHTTPEndpoint.serviceStreamingRequest(BaseStreamingHTTPEndpoint.java:1022)
 at flex.messaging.endpoints.BaseStreamingHTTPEndpoint.service(BaseStreamingHTTPEndpoint.java:430)
 at flex.messaging.MessageBrokerServlet.service(MessageBrokerServlet.java:322)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
 at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1093)
 at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
 at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:121)
 at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
 at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
 at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
 at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
 at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712)
 at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
 at com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:54)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
 at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:313)
 at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
 at org.mortbay.jetty.Server.handle(Server.java:313)
 at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:506)
 at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:844)
 at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:644)
 at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
 at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:381)
 at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:396)
 at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442)
コメントしてビルドしなおし、再びStreaimingAMFChannelを使って接続を試してみると・・・

java.security.AccessControlExceptionが出ない(>w<)!
やった!GAE/Jのセキュリティサンドボックスにひっかからずにいけたみたい!

ローカルのGAE/Jのプロジェクトで、StreamingAMFChannelを使って各種ブラウザとチャット通信を試してみる。IE8、Firefox、Chrome、Safari、Opera、Sleipnir、Lunascape5など。

IE系エンジンを使ったブラウザでは、consumer.subscribe();しても反応が無く、再読み込みして再度subscribeすると
[BlazeDS]Endpoint with id 'my-streaming-amf' cannot grant streaming connection to FlexClient with id 'F1B77645-EE1C-F1B8-66AA-7714CC07D614' because max-streaming-connections-per-session limit of '1' has been reached.
とエラーが出て、Consumerの接続に失敗する。IEならではのセッション周りの問題かと思い、BlazeDSのドキュメントを参考に、services-config.xmlのStreamingAMFChannelの設定にpropertiesを追加する。
        
            
            
              
                
                
              
            
        
IE系エンジンでも無事にsubscribe出来るようになった。

う~ん、さすがStreaming。文字列表示がサックサク。pollingの場合は文字列が表示されるまで一呼吸ある感じがモッサリして見えるがStreamingはそんなことはない。やっぱりStreamingの方がNearリアルタイム通信だなぁとルンルンでGAE/J上にデプロイしてみると・・・

動かない・・・orz

ぬかった。ぬかった。完全にぬか喜びだった。ノー。ConsumerもProducerもchannelFaultが発生。GAE/Jの管理パネルからログを見てみると、
[<GAE/Jアプリケーション名>/1.336207119101686921].: [BlazeDS]Endpoint with id 'my-streaming-amf' cannot service the streaming request as either the supplied FlexClient id 'F2421357-2D0B-B9BA-A2B2-6F5794E9859B is not valid, or the FlexClient with that id is not valid.
というログが残っていた。このエラーメッセージを表示していたflex.messaging.endpoints.BaseStreamingHTTPEndpoint.javaの1005行目から1019行目までをコメントしてみたらどうかということに(^^;ソースいじり放題だな)
/*if (!command.equals(CLOSE_COMMAND) && !validFlexClientId)
        {
            if (Log.isError())
                log.error("Endpoint with id '" + getId() + "' cannot service the streaming request as either the supplied"
                        + " FlexClient id '" + flexClientId + " is not valid, or the FlexClient with that id is not valid.");

            try
            {
                // Return an HTTP status code 400 to indicate that the client's request was syntactically invalid (invalid id).
                res.sendError(HttpServletResponse.SC_BAD_REQUEST);
            }
            catch (IOException ignore)
            {}
            return; // Abort further server processing.
        }*/
またビルドしなおしてローカルで動作確認→OK!→GAE/Jにデプロイ→GAE/J上で動作確認→失敗orz

ログにエラーは出なくなったが、Consumerにsubscribeしてもウンともスンとも言わない。もちろんチャットのメッセージを書いて送っても何のリアクションもない。動かない。。。


つまりはこれがクラウドや分散環境ということかもしれない。

Streamingのように静的に特定のサーバーとコネクションをはり続けるようなアプリケーションは構造的にムリというか向かないということだろう。1つのクライアントからのセッション情報が重複してしまうという最初のトラブルからそもそもそういうことだったのだ。

セッションがGAE/Jでは使えないということではないが、特定のサーバーとずっと通信するのではなく、分散環境で、どのサーバーと通信されているのか分からないし知らなくてもいいようなクラウドの世界では、セッションメインのWebアプリケーションの作成手法は一旦頭から追い出さないといけない。自分でサーバーもメンテし運用していると、そういう従来のクセがついつい出てしまう。

やってみてわかることもあるものだ。