Flexと JRubyで作るリアルタイム投票システム
※音が出るので注意※
シンプルな人気投票のプログラムなんだけど、Flex/BlazeDSのメッセージング機能により他人の投票もリアルタイムに見ることが出来るのが特徴。
操作してみたい人はこちら(期間限定、飽きたら消えるので悪しからず)
微妙なリアルタイム投票システム
多人数で操作したほうが楽しいので皆様お誘い合わせの上どうぞ。
一人でもブラウザを複数枚開いて操作してみれば意味がわかるかも(うるさいけど)。
あと、右クリックで View Sourceも出来るようにしてみた。
技術的トピックなど
サーバ側ソース
Flexに対する公開インターフェース ( VoteService )
package net.stbbs.jrubytest;
import java.util.Map;
public interface VoteService {
public Map getProgress();
public void doVote(String name, String comments);
}
VoteServiceの実装が下記。Springの Bean定義ファイル内にそのまま Rubyで書き込んでいる。なお、標準Rubyライブラリや RubyGemsを使うためにアプリケーションサーバに対して VM引数 jruby.homeの設定をしてやる必要がある。
<lang:jruby id="voteService"
script-interfaces="net.stbbs.jrubytest.VoteService"
init-method="init">
<lang:inline-script>
<![CDATA[
require 'rubygems'
require 'active_record'
include_class 'flex.messaging.MessageBroker'
include_class 'flex.messaging.util.UUIDUtils'
include_class 'flex.messaging.messages.AsyncMessage'
class Nomination < ActiveRecord::Base
has_many :comments, :order=>"id"
end
class Comment < ActiveRecord::Base
belongs_to :nomination
end
class VoteService
def setJndiName(jndiName)
@jndiName = jndiName
end
def setDDL(ddl)
@ddl = ddl
end
def init
#logger = ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.allow_concurrency = true
ActiveRecord::Base.establish_connection(
:jndi=>@jndiName, :adapter => 'jdbch2')
begin
ActiveRecord::Base.connection.execute(@ddl)
Nomination.transaction {
Nomination.create(:name=>"konata", :full_name=>"泉 こなた")
Nomination.create(:name=>"kagami", :full_name=>"柊 かがみ")
Nomination.create(:name=>"tsukasa",:full_name=>"柊 つかさ")
Nomination.create(:name=>"miyuki",:full_name=>"高良 みゆき")
}
rescue Exception=>e
print e.backtrace
raise e
end
end
def getProgress
progress = Hash.new
Nomination.find(:all).each {|n|
progress[n.name] = {
:full_name=>n.full_name,
:votes_obtained=>n.votes_obtained,
:recent_comments=>n.comments.last(5).collect {|c|
{:comment=>c.comment}
}
}
}
progress
end
def doVote(name, comment)
Nomination.transaction {
n = Nomination.find_by_name(name)
return if n == nil
if comment != nil && comment != "" then
Comment.create(:nomination_id=>n.id, :comment=>comment)
end
n.votes_obtained += 1
n.save
}
broker = MessageBroker.getMessageBroker(nil)
msg = AsyncMessage.new
msg.setDestination("vote")
msg.setMessageId(UUIDUtils.createUUID)
msg.setBody({:name=>name,:progress=>getProgress})
broker.routeMessageToService(msg, nil)
end
end
VoteService.new
]]>
</lang:inline-script>
<lang:property name="jndiName" value="java:/comp/env/jdbc/vote"/>
<lang:property name="DDL">
<value>
<![CDATA[
drop table if exists nominations;
create table if not exists nominations (
id integer IDENTITY primary key,
name varchar not null,
full_name varchar not null,
votes_obtained integer default 0
);
create unique index nominations_idx on nominations(name);
drop table if exists comments;
create table if not exists comments (
id integer IDENTITY primary key,
nomination_id integer not null,
comment varchar not null,
created_at timestamp not null
);
]]>
</value>
</lang:property>
</lang:jruby>
BlazeDSの設定
BlazeDSの設定は WEB-INF/flex/services-config.xmlに書くのが通常だが、Springの applicationContext.xmlで設定してしまえたほうが個人的に気分が良かったので Spring用のカスタム ConfigurationManagerを書いた。これはそれを使った場合の設定。なので自分にしか役に立たない。ごめん。
net.stbbs.blazeds.springパッケージは時間が出来たら githubあたりで公開したいと思うんだけど、あきらかに自分が使わない設定項目については対応していないし対応する気にもならないのでどうしたものか。
<!--
BlazeDS用コンフィギュレーション
MessageBrokerServletの services.configuration.managerパラメータに
net.stbbs.blazeds.spring.FlexSpringConfigurationManagerを与えると、
WEB-INF/flex/services-config.xmlの代わりにこちらが使われる
-->
<bean class="net.stbbs.blazeds.spring.MessagingConfiguration">
<property name="channelSettings">
<list>
<!-- 非ポーリングAMFチャンネル (RPC用) -->
<bean class="net.stbbs.blazeds.spring.AMFChannelSettings">
<constructor-arg value="my-amf"/>
<!-- mx:RemoteObjectの endpointプロパティにセットするURL -->
<property name="uri"
value="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"/>
</bean>
<!-- ポーリングAMFチャンネル (Pub/Sub用) -->
<bean class="net.stbbs.blazeds.spring.AMFPollingChannelSettings">
<constructor-arg value="my-polling-amf"/>
<!-- mx:AMFChannel の uri プロパティにセットするURL -->
<property name="uri"
value="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpolling"/>
</bean>
</list>
</property>
<property name="serviceSettings">
<list>
<!-- リモーティング(RPC)サービス -->
<bean class="net.stbbs.blazeds.spring.RemotingServiceSettings">
<constructor-arg value="remoting-service"/>
<property name="adapterSettings">
<list>
<bean class="net.stbbs.blazeds.spring.JavaAdapterSettings">
<constructor-arg value="java-object"/>
<property name="default" value="true"/>
</bean>
</list>
</property>
<!-- RPCサービスでは非ポーリングAMFを使用 -->
<property name="defaultChannels">
<list>
<value>my-amf</value>
</list>
</property>
<!-- Destinationアノテーション付き、又は RemotingDestinationインターフェイスつきの
Beanを自動的に destinationとして登録する -->
<property name="destinationsByAnnotation" value="true"/>
</bean>
<!-- Pub/Subサービス -->
<bean class="net.stbbs.blazeds.spring.MessageServiceSettings">
<constructor-arg value="messaging-service"/>
<property name="adapterSettings">
<list>
<bean class="net.stbbs.blazeds.spring.ActionScriptAdapterSettings">
<constructor-arg value="actionscript"/>
<property name="default" value="true"/>
</bean>
</list>
</property>
<!-- Pub/SubサービスではポーリングAMFを使用 -->
<property name="defaultChannels">
<list>
<value>my-polling-amf</value>
</list>
</property>
<!-- destination(JMSでいうところの Topic)を定義 -->
<property name="destinationNames">
<list>
<value>vote</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
個人的なメモ
activerecord-jdbc-adapterはバージョン 0.8から JRuby 1.1専用になってしまった。仕方ないので DBMS特有のモジュールも含めバージョン 0.7.2をインストールする必要がある。
jruby -S gem install activesupport
jruby -S gem install activerecord
jruby -S gem install -r activerecord-jdbc-adapter -v 0.7.2
jruby -S gem install -r activerecord-jdbch2-adapter -v 0.7.2
Gentoo(Portage)の JRuby 1.0.3は Java 1.6.0で動かせないみたい。仕方ないのでデフォルトJVMを 1.5.0に切り替え。
rubygemsをインストールしようとしたら OutOfMemoryErrorが出る。仕方ないので/usr/bin/jrubyを編集してVMオプションに -Xmx512mをつけた。そしたら 512でも足りなかった。64bitだからってそんなに使いますか。1024で解決。
PortageのJRubyGemsから参照されているutf8procのネイティブ(utf8proc_native.so)部が怪しくてActiveSupportをロードしたときにエラーが出る。lddで見てみたが特に外のライブラリ(iconvとか)に依存してるわけではなさそうで、外的要因じゃなければと仕方なく /usr/share/jruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/multibyte/chars.rbの最後の方で「ネイティブ版のutf8procが使えたらそっちを使う」ってなってる所を手で削除した。
関連記事
- Springのスクリプト言語サポートを使って BlazeDSと JRubyを統合する
- 実際に BlazeDS経由で ActionScript3から呼び出せるように Rubyスクリプトを配置する方法
- なぜ Flex向けのサービスをRubyで記述することにこだわるのか
- こっちの記事は Flex - JRubyの実用的なシチュエーションについて

0 件のコメント:
コメントを投稿
<< ホーム