11 分のあれに 5 分くらいでウェブサービス API を追加するシナリオ
http://itoshi.tv/d/?date=20060506#p01 に ActionWebService (AWS) をつかってウェブサービス API を追加してみた.タイプするだけなら 5 分くらいでできるシナリオである.
- http://itoshi.tv/d/?date=20060506#p01
- http://manuals.rubyonrails.com/read/chapter/67 AWDwR の Chapter 20 Web Services on Rails を参考にした.
Agile Web Development With Rails: A Pragmatic Guide (The Facets Of Ruby Series)
- 作者: David Thomas,David Heinemeier Hansson,Leon Breedt
- 出版社/メーカー: Pragmatic Bookshelf
- 発売日: 2005/09/22
- メディア: ペーパーバック
- クリック: 18回
- この商品を含むブログ (56件) を見る
- http://idm.s9.xrea.com/ratio/2006/04/17/000414.html が AWS の雰囲気をつかむのに大変参考になった.
仕様
今回のウェブサービス API の仕様は,データベースのエントリを ID で取得したり,ID のリストを取得する基本的で簡単なものを考える.
- API 名:Expression
- affyid の発現量を取得するメソッド:find_expressions_by_id(affyid)
- 引数 affyid は String
- 返り値は Float の Array
- affyid の任意の day の発現量を取得するメソッド:find_expression_by_id(affyid, day)
- 引数 affyid は String
- 引数 day は Int
- 返り値は Float
- affyid のリストを取得するメソッド:find_all_affyid
- 引数はなし
- 返り値は String の Array
次に,ウェブサービスのためのコードを生成する.なお,以降の作業は実働する11 分のあれのディレクトリ expview の中でおこなう.
ruby script/generate web_service Expression find_expression_by_id find_expressions_by_id find_all_affyids
仕様にもとづいて API 定義 app/api/expression_api.rb を埋める.
生成された状態.
class ExpressionApi < ActionWebService::API::Base api_method :find_expression_by_id api_method :find_expressions_by_id api_method :find_all_affyids end
こうする.
class ExpressionApi < ActionWebService::API::Base api_method :find_expression_by_id, :expects => [{:affyid => :string}, {:day => :int}], :returns => [:float] api_method :find_expressions_by_id, :expects => [{:affyid => :string}], :returns => [[:float]] api_method :find_all_affyids, :expects => [], :returns => [[:string]] end
expects に引数の名前と型情報,returns に返り値の型情報を Hash の形で記述する.DSL 風味.
つぎに,仕様にもとづいて API のコントローラ app/controllers/expression_controller.rb を書く.
生成された状態.
class ExpressionController < ApplicationController wsdl_service_name 'Expression' def find_expression_by_id end def find_expressions_by_id end def find_all_affyids end end
こうする.
class ExpressionController < ApplicationController wsdl_service_name 'Expression' web_service_scaffold :invoke def find_expression_by_id(affyid, day) @expression = Expression.find_by_affyid(affyid) if [0, 2, 4, 10].include?(day) @expression["d#{day}"] else -1.0 end end def find_expressions_by_id(affyid) @expression = Expression.find_by_affyid(affyid) ['d0', 'd2', 'd4', 'd10'].map {|m| @expression[m] } end def find_all_affyids Expression.find(:all).map {|x| x.affyid } end end
サーバの起動と web service scaffold で動作確認
ruby script/server open http://localhost:3000/expression/invoke
app/controllers/expression_controller.rb の web_service_scaffold :invoke によって,ウェブサービスのクライアントが提供されている.なお,open コマンドは Mac OS X 付属のものである.
ここで注意する必要があるのは find_all_affyids() は返り値が非常に大きいため,レンダリングに非常に多くのリソースを必要とするのでブラウザが固まりがち.invoke ボタンをクリックしてはいけない.
これくらいの API だったら 5 分くらいでタイプできるでしょう.
SOAP::WSDLDriver をつかったクライアント
SOAP::WSDLDriver をつかったクライアントの例.ウェブサービスで利用できるメソッドは methods(false) で調べることができる.
require 'soap/wsdlDriver' wsdl_uri = "http://localhost:3000/expression/service.wsdl" factory = SOAP::WSDLDriverFactory.new(wsdl_uri) driver = factory.create_driver method_list = driver.methods(false) p method_list #=> ["findExpressionsById", "FindAllAffyids", "findAllAffyids", "FindExpressionById", "findExpressionById", "FindExpressionsById"] expression = driver.findExpressionById("100015_at", 0) p expression #=> 5.55660043997955 expression = driver.send("FindExpressionById", "100015_at", 0) p expression #=> 5.55660043997955 expressions = driver.findExpressionsById("100015_at") p expressions #=> [5.55660043997955, 5.6629197160292, 5.60480888509297, 5.72528846920502] affyid_list = driver.findAllAffyids() p affyid_list #=> ["100001_at", "100002_at", "100003_at", ...
AWS でつくった WSDL なウェブサービスを Taverna から利用する
WSDL の提供されているウェブサービスは Taverna で容易に追加して扱うことができる.id:nakao_mitsuteru:20060511:AWS でウェブサービス API を追加した expview を試してみた.
- Apache Taverna - Apache Taverna (incubating)
- http://itoshi.tv/d/?date=20060506#p01
- 11 分のあれに 5 分くらいでウェブサービス API を追加するシナリオ - 研究開発創作日誌
使用環境
- Mac OS X 10.4.6
- Taverna.app 1.3.2-RC1 http://prdownloads.sourceforge.net/taverna/taverna-workbench-1.3.2-RC1.dmg?download
- ruby 1.8.4
- gem 0.8.11
- rails 1.1.2
expview の起動
id:nakao_mitsuteru:20060511:AWS の API 付きの expview を起動する.
$ cd expview $ ruby script/server => Booting WEBrick... => Rails application started on http://0.0.0.0:3000 => Ctrl-C to shutdown server; call with --help for options [2006-05-15 00:02:57] INFO WEBrick 1.3.1 [2006-05-15 00:02:57] INFO ruby 1.8.4 (2005-12-24) [i686-darwin8.6.1] [2006-05-15 00:02:57] INFO WEBrick::HTTPServer#start: pid=1559 port=3000
WSDL を確認する.
$ open http://localhost:3000/expression/service.wsdl
Safari.app の場合は真っ白なページになるけど,ソースを表示(opt + cmd + u)で WSDL が読める.
Taverna に WSDL を登録
Taverna project website: Documentation Home の Basic Introdutory Tutorial (pdf) を読んでいないと以下の内容は分からないかもしれません.
- Taverna.app 起動.
- Taverna の Available services ウィンドウの Available Processors のうえで右クリック(もしくは ctrl + クリック)で Available Processors メニュー を表示.
- Available Processors メニューの Add New WSDL scavenger... を選択.
- WSDL URI (http://localhost:3000/expression/service.wsdl) を登録.
- Available Processors ウィンドウに WSDL @ http://localhost:3000/expression/service.wsdl が追加されればオッケー.
FindExpressionsById
- Available services ウィンドウの WSDL @ http://localhost:3000/expression/service.wsdl を展開して FindExpressionsById を表示.
- FindExpressionsById を右クリック(もしくは ctrl + クリック)してメニューを表示.
- メニューの中の invoke を選択.
- Run Workflow ウィンドウが表示される.
- Run Workflow ウィンドウの Input Document パネルの affyid を右クリック(もしくは ctrl + クリック)して New Input Value を選択.
- 右のパネルの Some input data goes here を適当な affyid(例えば 100015_at) に書き換える.
- Run Workflow ウィンドウの右下の Run Workflow をクリックして実行.
- Enactor invocation ウィンドウが表示される.
ところが,ここで processor が ServiceFallure になる.困りました.
Enactor invocation の Process Report
エラーを確認する.
<workflowReport workflowID="2" workflowStatus="COMPLETE"> <processorList> <processor name="processor"> <ServiceFailure TimeStamp="2006/05/15 0:37:44" /> <ServiceError Message="Error occured during invocation [D" TimeStamp="2006/05/15 0:37:44">Error occured during invocation [D<br> WSDLInvocationTask.execute(..) : l ine 192 &lt;WSDLInvocationTask.java&gt;<br> ProcessorTask.runAndGenerateTemplates(..) : line 503 &lt;ProcessorTask.java&gt;<br> ProcessorTask.doI nvocationWithRetryLogic(..) : line 453 &lt;ProcessorTask.java&gt;<br> ProcessorTask.invokeOnce(..) : line 376 &lt;ProcessorTask.java&gt;<br> Proc essorTask.invokeWithoutIteration(..) : line 561 &lt;ProcessorTask.java&gt;<br> ProcessorTask.invoke(..) : line 301 &lt;ProcessorTask.java&gt;<br> ProcessorTask.handleRun(..) : line 237 &lt;ProcessorTask.java&gt;<br> NewState$1.run(..) : line 67 &lt;NewState.java&gt;<br><br>[D<br> DataThingFactory.convertObject(..) : line 146 &lt;DataThingFactory.java&gt;<br> DataThing.<init>(..) : line 234 &lt;DataThing.java&gt;<br> WSDLInvocationTask.execute(..) : line 134 &lt;WSDLInvocationTask.java&gt;<br> ProcessorTask.runAndGenerateTemplates(..) : line 503 &lt;ProcessorTask.java& amp;gt;<br> ProcessorTask.doInvocationWithRetryLogic(..) : line 453 &lt;ProcessorTask.java&gt;<br> ProcessorTask.invokeOnce(..) : line 376 &lt;Proces sorTask.java&gt;<br> ProcessorTask.invokeWithoutIteration(..) : line 561 &lt;ProcessorTask.java&gt;<br> ProcessorTask.invoke(..) : line 301 &lt;P rocessorTask.java&gt;<br> ProcessorTask.handleRun(..) : line 237 &lt;ProcessorTask.java&gt;<br> NewState$1.run(..) : line 67 &lt;NewState.java&am p;gt;<br></ServiceError> <Invoking TimeStamp="2006/05/15 0:37:44" /> <ProcessScheduled TimeStamp="2006/05/15 0:37:44"> <s:arbitrarywsdl xmlns:s="http://org.embl.ebi.escience/xscufl/0.1alpha"> <s:wsdl>http://localhost:3000/expression/service.wsdl</s:wsdl> <s:operation>FindExpressionsById</s:operation> </s:arbitrarywsdl> </ProcessScheduled> </processor> </processorList> </workflowReport>
対処法
FindExpressionsById は返り値は Float の Array です.試行錯誤の結果,これを String の Array にすると値は(文字列としてですが)取り出すことができます.
その場合は expview の二つのファイルを変更する必要があります.
app/api/expression_api.rb の :returns の float を string に変更
class ExpressionApi < ActionWebService::API::Base api_method :find_expression_by_id, :expects => [{:affyid => :string}, {:day => :int}], :returns => [:string] api_method :find_expressions_by_id, :expects => [{:affyid => :string}], :returns => [[:string]] api_method :find_all_affyid, :expects => [], :returns => [[:string]] end
app/controllers/expression_controller.rb のメソッドの返り値に to_s を追加.
class ExpressionController < ApplicationController wsdl_service_name 'Expression' web_service_scaffold :invoke def find_expression_by_id(affyid, day) @expression = Expression.find_by_affyid(affyid) if [0, 2, 4, 10].include?(day) @expression["d#{day}"].to_s else -1.0 end end def find_expressions_by_id(affyid) @expression = Expression.find_by_affyid(affyid) ['d0', 'd2', 'd4', 'd10'].map {|m| @expression[m].to_s } end def find_all_affyid Expression.find(:all).map {|x| x.affyid } end end
再試行
- Taverna.app を一度終了してから起動し直す.そうしないと,あたらしい WSDL を読み込まない.
- Available services ウィンドウの WSDL @ http://localhost:3000/expression/service.wsdl を展開して FindExpressionsById を表示.
- FindExpressionsById を右クリック(もしくは ctrl + クリック)してメニューを表示.
- メニューの中の invoke を選択.
- Run Workflow ウィンドウが表示される.
- Run Workflow ウィンドウの Input Document パネルの affyid を右クリック(もしくは ctrl + クリック)して New Input Value を選択.
- 右のパネルの Some input data goes here を適当な affyid(例えば 100015_at) に書き換える.
- Run Workflow ウィンドウの右下の Run Workflow をクリックして実行.
- Enactor invocation ウィンドウが表示される.
- Result browser パネルが表示されると,正常に実行されています.
- 左側のパネルをクリックして値を表示.affyid が 100015_at ならこんな値.
Self 5.55660043997955 5.6629197160292 5.60480888509297 5.72528846920502
まとめ
すべてが Taverna に(繋がるように)なる,を実践するのに Ruby on Rails の Action Web Service (AWS) を使うとローカルで使っているサービスを手軽に扱うことができる.
問題点:AWS と Taverna で型変換上の問題がある.Float が扱えていない.
GUI アプリの解説をテキストだけでしてもなんだかよく分からない.
課題:ソースコードの一部をハイライトして表示したいけどやり方がわからない.