Flex⇔Perl連携 その2
仕様
簡単すぎるサンプルだとつまらないので、
そこそこ実践的(?)な仕様にしてみました。
クライアントサイド
○FlexToPerl.mxml
そんなに難しい事はしてません。
JSON/XMLはHTTPServiceで、AMF0はNetConnectionでデータを取ります。
AMF0のソースは、こちらのクライアントコードを参考にしてます。*1
○JSONClient.as/XMLClient.as/AmfClient.as
フローは基本一緒です。
- 指定したURLにリクエスト(検索条件のパラメータを渡す)
- 取得した結果をデータグリッドにバインド
AMFClientだけは、テスト的に「echo」「sum」「list」の3つのサーバメソッドを呼び出しています。
データグリッドに入力されるのは「listResult」メソッドです。
private function listResult(result:*):void { // Empテーブルのハッシュ var _result:Array = result as Array; var _dp:ArrayCollection = new ArrayCollection; for (var i:int =0; i<_result.length;i++){ var _item:Object = _result[i]; _dp.addItem(_item); } view.dg.dataProvider = _dp; }
ここでは、サーバからリストのハッシュとして渡されてきたデータをArrayCollectionに変換し、
データグリッドにバインドしています。
ちなみに、渡ってくるデータ(resultに入ってくるデータ)はこんな感じです。
$VAR1 = { 'sal' => '800', 'emp_no' => '7369', 'dept_id' => '2', 'mgr_id' => '13', 'hiredate' => '1980-12-17', 'emp_name' => 'SMITH', 'id' => '1', 'version_no' => '1' }; $VAR2 = { 'sal' => '1250', 'emp_no' => '7654', 'dept_id' => '3', 'mgr_id' => '6', 'hiredate' => '1981-09-28', 'emp_name' => 'MARTIN', 'id' => '5', 'version_no' => '1' };
○JSONの注意点
JSONをデコードするのにライブラリが必要です。
http://code.google.com/p/as3corelib/
こちらからダウンロードしてプロジェクトに配置して下さい。
サーバサイド
○テーブル作成
まず、最初にテーブルを作ってしまいましょう。
テーブルは以前S2Flex2で利用したサンプルSQLをそのまま使います。
○DBアクセス
次に、DBアクセスモジュールを作ります。
手軽にやりたいのでDBIx::Class::Schema::Loaderを使う事にします。
Postgres使ってます。
$ module-starter --module=AmfTestDBIx $ cd AmfTestDBIx $ perl -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:./lib -e 'make_schema_at("AmfTestDBIx::Schema", { constraint => qr/emp/, debug => 1 },[ "dbi:Pg:dbname=test","postgres" ])'
これだけでEMPテーブルにアクセスするモジュールを作成してくれます。*2
AmfTestDBIx/lib ディレクトリ構成はこんな感じ。
lib |-- AmfTestDBIx | |-- Schema | | `-- Emp.pm | `-- Schema.pm `-- AmfTestDBIx.pm
次にコネクション情報を AmfTestDBIx/Schema.pm に記述します。
__PACKAGE__->connection( 'dbi:Pg:dbname=test;host=localhost', 'postgres', '', { AutoCommit => 0 } );
ResultSetをハッシュで扱いたいので、AmfTestDBIx/Schema/Emp.pm にプラグイン(AsFdat)を追加します。
__PACKAGE__->load_components( "AsFdat", "PK::Auto", "Core", );
DBアクセスはこんな感じでOK。
○サーバスクリプト
次に簡易サーバですが、前回の simple_server_mod.pl を元にして書いてみました。
amf_dbix_all_server.pl
#!/usr/bin/perl use strict; use warnings; use HTTP::Engine; use Data::AMF::Packet; use List::Util (); use FindBin::libs; use Path::Class; use URI::Escape; use Data::Dumper; use lib '/home/toritori0318/AmfTestDBIx/lib'; use AmfTestDBIx::Schema; use DBIx::Class::AsFdat; sub getEmp { my $prm = shift; $prm = +{ map { ( $_, '%' . $prm->{$_} . '%' ) } keys %$prm }; my $schema = AmfTestDBIx::Schema->connect; my @rs = map { $_->as_fdat } $schema->resultset('Emp')->search({ 'emp_name' => { like => $$prm{emp_name} }}); return \@rs; } &main; exit; sub main { my $engine = HTTP::Engine->new( interface => { module => 'ServerSimple', args => { host => 'localhost', port => '3000', }, request_handler => \&handler, }, ); $engine->run; } sub handler { my $req = shift; my $qq = cnv_hash( $req->uri->query ) if $req->uri->query; if ( $req->uri->path eq '/gateway' ) { my $fh = $req->body; my $body = do { local $/; <$fh> }; my $request = Data::AMF::Packet->deserialize($body); my @result; for my $message ( @{ $request->messages } ) { my $method = __PACKAGE__->can( $message->target_uri ); if ($method) { my $result = $method->( $message->value ); push @result, $message->result($result); } } my $response = Data::AMF::Packet->new( version => $request->version, headers => [], messages => \@result, ); return res( 'application/x-amf', $response->serialize ); } elsif ( $req->uri->path eq '/json' ) { use JSON; return res( 'application/xhtml+xml', to_json( &getEmp( $qq ) ) ); } elsif ( $req->uri->path eq '/xml' ) { use XML::Simple; my $xml = new XML::Simple; my $body = '<?xml version="1.0" encoding="utf-8" ?>\n'; my $emp = &getEmp($qq); if(@$emp > 0){ my $emp_rec = {record => $emp}; $body .= XMLout( $emp_rec, RootName=>'Result', NoAttr => 1 ); }else{ $body .= "<Result><record></record></Result>"; } return res( 'application/xhtml+xml', $body ); } else { return res( 'application/x-shockwave-flash', scalar file('./examples/simple_flash_remoting.swf')->slurp ); } } sub res { my ( $content, $body ) = @_; HTTP::Engine::Response->new( content_type => $content, body => $body ); } sub echo { return $_[0]; } sub sum { return List::Util::sum( @{ $_[0] } ); } sub dump { use YAML; warn Dump $_[0]; } sub list { my $prm = shift; my $hash = $$prm[0]; #my %hash = %{ $$prm[0] }; return getEmp($hash); } sub cnv_hash { my $qs = shift; my %ret = (); my @pairs = split( /&/, $qs ); foreach my $pair (@pairs) { my ( $name, $value ) = split( /=/, $pair ); $ret{uri_unescape($name)} = uri_unescape($value); } return \%ret; } 1;
まず handler ですが、JSON/XMLの場合はリクエストURLでフォーマットを判断し、レスポンスを返します。
AMFの場合はクライアントで指定されたメソッドを実行し、その結果をAMFフォーマットにしてレスポンスを返しています。
レスポンスデータについては、先ほど作成したDBアクセスモジュールを呼び出し、
その結果を各々のフォーマットに変換しています。
動かす
まず、前回と同じようにサーバ側でamf_dbix_all_server.plを実行します。
]$ perl amf_dbix_all_server.pl HTTP::Engine::Interface::ServerSimple : You can connect to your server at http://localhost:3000/
あとはFlexから検索実行するとこんな感じできちんとデータ取れてます!
いい感じ。
今回使用したスクリプトもgithubに置きました。*3
http://github.com/toritori0318/Flex_Perl/tree/master
今回触った感じだと、JSONがかなりとっつきやすい。
ちょっとしたもの*4はこれでやるのが吉っぽいなぁ。
AMF0は他の方の記事を見るとそんなに利点なさそうなので
JSONでいいんじゃね?と思ってしまった。
余裕があったらベンチマークも出してみたいです。
次回ですが、今回のFlexアプリケーションをCatalystと連携させてみたいと思います。
(その前に番外編があるかも)