2008年6月28日

Google App Engine + Flex

Google App Engine を始めて見た。

スタートガイド通りに動いたので、試しにFlexで作り変えてみた。
で、通信にAMFを使ってみたいと思い、PyAMFを使ってみる。


まず一つ覚え書き。
app.yaml に書くapplication名や、templateを使ってファイル名を指定するときに、大文字を使っては行けない。

なぜか、application名に大文字を使ったり、templateに使うファイルのファイル名に大文字が入ってるとうまく動かなかったなぜだ?

プログラムは、メインページの表示にmain.pyを用い、AMFの送受信、データ登録処理をgateway.pyで。
Flexからは、gateway.pyに対してデータを送信。

gateway.pyからGreetingの検索結果データを送信する際、String型やDate型はそのまま送れたが、User型の送り方が分からなかった。
で、送信する前に、nicknameを抜き出し、String型にした後、送信用のGreetingクラスを用意して、データの送信。
なんだか、不細工だなぁ。

で、以下はコード。
app.yaml



application: [application name]
version: 1
runtime: python
api_version: 1

handlers:
- url: /gateway
script: gateway.py

- url: /history
static_dir: history

- url: /AC_OETags.js
static_files: AC_OETags.js
upload: AC_OETags.js

- url: /[swf file name]
static_files: [swf file name]
upload: [swf file name]

- url: /srcview
static_dir: srcview

- url: .*
script: main.py



main.py


#!/usr/bin/env python
import wsgiref.handlers
import os

from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.api import users
from google.appengine.ext import db

class Greeting(db.Model):
author = db.UserProperty()
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)

class MainHandler(webapp.RequestHandler):
def get(self):
user = users.get_current_user()

if user:
path = os.path.join(os.path.dirname(__file__), '[flex html file name]')
self.response.out.write(template.render(path, None, debug=True))
else:
self.redirect(users.create_login_url(self.request.uri))

def main():
application = webapp.WSGIApplication([('/', MainHandler)], debug=True)
wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
main()



gateway.py


#!/usr/bin/env python

import wsgiref.handlers

from pyamf.remoting.gateway.wsgi import WSGIGateway
from google.appengine.api import users
from google.appengine.ext import db

import pyamf
from pyamf.flex import ArrayCollection, ObjectProxy

class Greeting(db.Model):
author = db.UserProperty()
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)

class Message():
pass

def build_message(greeting):
m = Message()
if greeting.author:
m.nickname = greeting.author.nickname()
else:
m.nickname = "Anonymous"

m.content = greeting.content
m.date = greeting.date

return m


def echoUser():
user = users.get_current_user()
if user:
return user.nickname()
else:
return "Anonymous"

def registor(data):
greeting = Greeting()

if users.get_current_user():
greeting.author = users.get_current_user()

greeting.content = data
greeting.put()
return "Success Registor"

def selectData():
greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 50")

ret = [ObjectProxy(build_message(greeting)) for greeting in greetings]

return ArrayCollection(ret)

services = {
'myservice.echoUser': echoUser,
'myservice.registor': registor,
'myservice.selectData': selectData,
}

def main():
application = WSGIGateway(services)
wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
main()



Flex


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" horizontalAlign="center"
initialize="initApp()" layout="vertical" viewSourceURL="srcview/index.html">

<mx:RemoteObject id="remoteObj" destination="myservice" endpoint="http://[Google App Engine address]/gateway">
<mx:method name="echoUser" result="onResultEcho(event)" fault="onFault(event)" />
<mx:method name="registor" result="onResult(event)" fault="onFault(event)" />
<mx:method name="selectData" result="onResultSelect(event)" fault="onFault(event)" />
</mx:RemoteObject>

<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

[Bindable]
private var dp:ArrayCollection = new ArrayCollection();

public function initApp():void{
callSelectData();
callEchoUser();
}

private function onResultEcho(re:ResultEvent):void{
usr_txt.text = re.result as String;
}

private function onResult(re:ResultEvent):void{
status_txt.text ="Send...";
callSelectData();
}

private function onFault(fault:FaultEvent):void{
status_txt.text = "Remoting error: ";
for(var d:String in fault.fault){
status_txt.text += fault.fault[d];
}
}

private function callRegistor():void{
var param:String = textIn.text;
remoteObj.registor(param);
}

private function callSelectData():void{
remoteObj.selectData();
}

private function onResultSelect(re:ResultEvent):void{
dp = re.result as ArrayCollection;
status_txt.text = "Load!";
}

private function callEchoUser():void{
remoteObj.echoUser();
}
]]>
</mx:Script>
<mx:VBox width="600">
<mx:HBox width="100%">
<mx:Text text="userID:"/>
<mx:Text id="usr_txt" text="" width="100%"/>
</mx:HBox>
<mx:DataGrid width="600" dataProvider="{dp}" height="100%">
<mx:columns>
<mx:DataGridColumn width="50" headerText="name" dataField="nickname"/>
<mx:DataGridColumn width="100" headerText="date" dataField="date"/>
<mx:DataGridColumn headerText="content" dataField="content"/>
</mx:columns>
</mx:DataGrid>
<mx:HBox width="100%">
<mx:TextInput id="textIn" width="100%"/>
<mx:Button label="Sign" click="callRegistor()"/>
</mx:HBox>
<mx:Text id="status_txt" x="10" y="224" text="" width="100%"/>
</mx:VBox>

</mx:Application>

2 コメント:

のんたん さんのコメント...

flexとGAEの連動を模索しています。
貴サイトの内容が日本のインターネットでは一番参考になると思います。

内容をコピペして実行しましたが、以下の内容で叱られてしまいます。

C:\Program Files\Google\google_appengine>dev_appserver.py pyamftest\
ERROR 2009-04-19 09:36:57,437 dev_appserver_main.py] Fatal error when loading
application configuration:
Invalid object:
Unknown url handler type.
URLMap
static_dir=None
script=None
url=/gateway
static_files=None
upload=None
expiration=None
login=optional
mime_type=None
>
in "pyamftest\app.yaml", line 8, column 1


8行目はapp.yaml
script: gateway.py

きっとgateway.py を書き直さないまま実行しているのが原因だと思いますが、よくわかりません よろしくお願いします。

のんたん さんのコメント...

自己解決しました!

お騒がせしました。