リモートプロシージャコール

□未翻訳

□翻訳中

□翻訳完了(Omi Chiba)

■レビュー(Yota Ichino)

リモートプロシージャコール

RPC

web2pyはどのような関数でもウェブサービスにする仕組みがあります。ここで言う仕組とは前述した仕組みと以下の場合で異なります:

web2py provides a mechanism to turn any function into a web service. The mechanism described here differs from the mechanism described before because:

  • 関数が引数を持つ場合

  • 関数がコントローラではなくモデルやモジュールで指定されている場合

  • RPCメソッドでサポートされている明示的な記述が必要な場合

  • より厳格なURLのネーミングが求められる場合

  • 固定のプロトコルの組み合わせで動くことで高い機能を実現する場合。同様の理由で拡張性は悪い。

    • The function may take arguments

    • The function may be defined in a model or a module instead of controller

    • You may want to specify in detail which RPC method should be supported

    • It enforces a more strict URL naming convention

    • It is smarter then the previous methods because it works for a fixed set of protocols. For the same reason it is not as easily extensible.

これらの機能を使うために:

To use this feature:

まず始めに、サービスオブジェクトをインポートしてインスタンス化します。

First, you must import and instantiate a service object.

1.

2.

from gluon.tools import Service

service = Service(globals())

これは雛形となるアプリケーション内にある"db.py”というモデルファイルの中で実装済みです。

This is already done in the "db.py" model file in the scaffolding application.

二つ目に、コントローラー内でサービスハンドラを公開します:

Second, you must expose the service handler in the controller:

1.

2.

3.

def call():

session.forget()

return service()

これは雛形となるアプリケーションのコントローラーにある"default.py"にて実装済みです。セッションクッキーを使用する場合はsession.forget()を削除します。

This is already done in the "default.py" controller of the scaffolding application. Remove session.forget() if you plan to use session cookies with the services.

三つ目に、サービスとして公開したいそれらの関数を装飾する必要があります。以下が現在サポートされているデコレータです:

Third, you must decorate those functions you want to expose as a service. Here is a list of currently supported decorators:

1.

2.

3.

4.

5.

6.

7.

8.

9.

@service.run

@service.xml

@service.json

@service.rss

@service.csv

@service.xmlrpc

@service.jsonrpc

@service.amfrpc3('domain')

@service.soap('FunctionName',returns={'result':type},args={'param1':type,})

例として、以下のデコレータ関数を考えてみましょう:

As an example, consider the following decorated function:

1.

2.

3.

@service.run

def concat(a,b):

return a+b

この関数はモデルまたはcallアクションが定義されているコントローラで定義できます。この関数は以下の2つの方法でリモートから実行できます。

This function can be defined in a model or in the controller where the call action is defined. This function can now be called remotely in two ways:

http://127.0.0.1:8000/app/default/call/run/concat?a=hello&b=world

http://127.0.0.1:8000/app/default/call/run/concat/hello/world

どちらの場合もhttpリクエストは以下の値を返します:

In both cases the http request returns:

1.

helloworld

@service.xmlデコレータが使用される場合は、以下のURL経由で実行されます:

If the @service.xml decorator is used, the function can be called via:

http://127.0.0.1:8000/app/default/call/xml/concat?a=hello&b=world

http://127.0.0.1:8000/app/default/call/xml/concat/hello/world

そしてXML形式の戻り値が出力されます:

and the output is returned as XML:

1.

2.

3.

<document>

<result>helloworld</result>

</document>

これがDAL Rowsオブジェクトだとしても関数の出力はシリアライズされ、実際、as_list()が自動で実行されます。

It can serialize the output of the function even if this is a DAL Rows object. In this case, in fact, it will call as_list() automatically.

@service.jsonデコレータが使用される場合は、以下のURL経由で実行されます:

If the @service.json decorator is used, the function can be called via:

http://127.0.0.1:8000/app/default/call/json/concat?a=hello&b=world

http://127.0.0.1:8000/app/default/call/json/concat/hello/world

そしてJSON形式の戻り値が出力されます。

and the output returned as JSON.

@service.csvデコレータが使用される場合は、サービスハンドラが、リストを含むリストのような反復可能オブジェクトを含む反復可能オブジェクトを、戻り値とする必要があります。例を挙げると:

If the @service.csv decorator is used, the service handler requires, as the return value, an iterable object of iterable objects, such as a list of lists. Here is an example:

1.

2.

3.

@service.csv

def table1(a,b):

return [[a,b],[1,2]]

このサービスは以下のどちらかのURLで実行されます:

This service can be called by visiting one of the following URLs:

http://127.0.0.1:8000/app/default/call/csv/table1?a=hello&b=world

http://127.0.0.1:8000/app/default/call/csv/table1/hello/world

そして戻り値は:

and it returns:

1.

2.

hello,world

1,2

@service.rssデコレータは前の章で説明した"generic.rss”と同様のフォーマトの値を見込んでいます。

The @service.rss decorator expects a return value in the same format as the "generic.rss" view discussed in the previous section.

それぞれの関数について複数のデコレータを持つことが可能です。

Multiple decorators are allowed for each function.

今までのところ、この章で説明した内容は前の章で説明した内容の単なる代替手段でしかありません。サービスオブジェクトの本当の力を発揮するのは後述するXMLRPC、JSONRPC、AMFRPCです。

So far, everything discussed in this section is simply an alternative to the method described in the previous section. The real power of the service object comes with XMLRPC, JSONRPC and AMFRPC, as discussed below.

XMLRPC

XMLRPC

例えば"default.py”コントローラに次のコードがあるとすると:

Consider the following code, for example, in the "default.py" controller:

1.

2.

3.

4.

5.

6.

7.

@service.xmlrpc

def add(a,b):

return a+b

@service.xmlrpc

def div(a,b):

return a/b

Pythonのシェルで以下のように実行できます

Now in a python shell you can do

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

>>> from xmlrpclib import ServerProxy

>>> server = ServerProxy(

'http://127.0.0.1:8000/app/default/call/xmlrpc')

>>> print server.add(3,4)

7

>>> print server.add('hello','world')

'helloworld'

>>> print server.div(12,4)

3

>>> print server.div(1,0)

ZeroDivisionError: integer division or modulo by zero

xmlrpclibモジュールはクライアントにXMLRPCプロトコルを提供します。web2pyはサーバーとして動作します。

The Python xmlrpclib module provides a client for the XMLRPC protocol. web2py acts as the server.

クライアントはServerProxy経由でサーバーに接続しサーバー上のデコレータ関数をリモートから実行できます。データ(a、b)はGET/POST変数経由ではなくXMLRPCプロトコルにより正しくエンコードされた形で関数に渡され、また、そうすることでデータタイプ(int、String、その他)の情報が保持されています。戻り値についても同様のことが言えます。さらに、サーバー上で発生した例外処理もクライアントに送信されます。

The client connects to the server via ServerProxy and can remotely call decorated functions in the server. The data (a,b) is passed to the function(s), not via GET/POST variables, but properly encoded in the request body using the XMLPRC protocol, and thus it carries with itself type information (int or string or other). The same is true for the return value(s). Moreover, any exception raised on the server propagates back to the client.

多くのプログラミング言語(C、 C++、Java、C#、Ruby、Perl)でXMLRPCライブラリは存在し、相互に使用されます。これは異なるプログラム言語間で通信をするアプリケーションを作成する場合に最適な方法のひとつです。

There are XMLRPC libraries for many programming languages (including C, C++, Java, C#, Ruby, and Perl), and they can interoperate with each other. This is one the best methods to create applications that talk to each other independent of the programming language.

XMLRPCクライアントはweb2pyのアクション内で実行することもできます。そうすることで、あるアクションが別のweb2pyアプリケーション(同じインストレーションであっても)とXMLRPCを利用して通信することができます。この場合はセッションのデッドロックに注意してください。同じアプリケーション内でXMLRPCを利用してアクションを実行する場合は、実行前にセッションロックを開放しなければなりません。

The XMLRPC client can also be implemented inside a web2py action, so that one action can talk to another web2py application (even within the same installation) using XMLRPC. Beware of session deadlocks in this case. If an action calls via XMLRPC a function in the same app, the caller must release the session lock before the call:

1.

JSONRPC and Pyjamas

JSONRPC

Pyjamas

JSONRPCはXMLRPCにとてもよく似ていますが、XMLの代わりにJSONベースのプロトコルを利用してデータをエンコードします。このアプリーケーションの例として、Pyjamasを使った手法を説明します。PyjamasはGoogle Web Toolkit(当初はJavaで書かれていた)のPython用ポートです。PyjamasによってPythonでクライアントアプリケーションを書くことができます。PyjamasはコードをJavaScriptに変換します。web2pyはJavaScriptを実行し、ユーザーのアクションによってクライアントから作成されたリクエストとAJAX経由で通信します。

JSONRPC is very similar to XMLRPC, but uses the JSON-based protocol instead of XML to encode the data. As an example of application here, we discuss its usage with Pyjamas. Pyjamas is a Python port of the Google Web Toolkit (originally written in Java). Pyjamas allows writing a client application in Python. Pyjamas translates this code into JavaScript. web2py serves the JavaScript and communicates with it via AJAX requests originating from the client and triggered by user actions.

ここではPyjamasをweb2py上でどのように動作させられるかを説明します。web2pyとPyjamas以外のライブラリは特に必要ありません。

Here we describe how to make Pyjamas work with web2py. It does not require any additional libraries other than web2py and Pyjamas.

JSONRPCを使ってサーバーと接続するPyjamasクライアント(全てJavaScript)を利用したシンプルな"todo”アプリケーションを作成していきます。

We are going to build a simple "todo" application with a Pyjamas client (all JavaScript) that talks to the server exclusively via JSONRPC.

まずはじめに、"todo”という名称のアプリケーションを作成します。

First, create a new application called "todo".

二つ目に、"models/db.py"にて、次のコードを記述します:

Second, in "models/db.py", enter the following code:

1.

2.

3.

db=DAL('sqlite://storage.sqlite')

db.define_table('todo', Field('task'))

service = Service(globals())

(注:Serviceクラスはgluon.toolsから提供されています)

(Note: Service class is from gluon.tools).

三つ目に、"contollers/default.py”にて、次のコードを記述します:

Third, in "controllers/default.py", enter the following code:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

def index():

redirect(URL('todoApp'))

@service.jsonrpc

def getTasks():

todos = db(db.todo.id>0).select()

return [(todo.task,todo.id) for todo in todos]

@service.jsonrpc

def addTask(taskFromJson):

db.todo.insert(task= taskFromJson)

return getTasks()

@service.jsonrpc

def deleteTask (idFromJson):

del db.todo[idFromJson]

return getTasks()

def call():

session.forget()

return service()

def todoApp():

return dict()

それぞれの関数の意味は明らかだと思います。

The purpose of each function should be obvious.

四つ目に、"view/default/todoApp.html"にて、次のコードを記述します:

Fourth, in "views/default/todoApp.html", enter the following code:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

<html>

<head>

<meta name="pygwt:module"

content="{{=URL('static','output/TodoApp')}}" />

<title>

simple todo application

</title>

</head>

<body bgcolor="white">

<h1>

simple todo application

</h1>

<i>

type a new task to insert in db,

click on existing task to delete it

</i>

<script language="javascript"

src="{{=URL('static','output/pygwt.js')}}">

</script>

</body>

</html>

このビューはまだ作成されていない"static/output/todoapp"のPyjamasコードを実行するだけです。

This view just executes the Pyjamas code in "static/output/todoapp" - code that we have not yet created.

五つ目に、"static/TodoApp.py"(todoAppでは無くTodoAppであることに注意!)にて、次のコードを記述します:

Fifth, in "static/TodoApp.py" (notice it is TodoApp, not todoApp!), enter the following client code:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

28.

29.

30.

31.

32.

33.

34.

35.

36.

37.

38.

39.

40.

41.

42.

43.

44.

45.

46.

47.

48.

49.

50.

51.

52.

53.

54.

55.

56.

57.

58.

59.

60.

61.

62.

63.

64.

65.

66.

67.

68.

69.

70.

71.

72.

73.

74.

75.

76.

77.

78.

from pyjamas.ui.RootPanel import RootPanel

from pyjamas.ui.Label import Label

from pyjamas.ui.VerticalPanel import VerticalPanel

from pyjamas.ui.TextBox import TextBox

import pyjamas.ui.KeyboardListener

from pyjamas.ui.ListBox import ListBox

from pyjamas.ui.HTML import HTML

from pyjamas.JSONService import JSONProxy

class TodoApp:

def onModuleLoad(self):

self.remote = DataService()

panel = VerticalPanel()

self.todoTextBox = TextBox()

self.todoTextBox.addKeyboardListener(self)

self.todoList = ListBox()

self.todoList.setVisibleItemCount(7)

self.todoList.setWidth("200px")

self.todoList.addClickListener(self)

self.Status = Label("")

panel.add(Label("Add New Todo:"))

panel.add(self.todoTextBox)

panel.add(Label("Click to Remove:"))

panel.add(self.todoList)

panel.add(self.Status)

self.remote.getTasks(self)

RootPanel().add(panel)

def onKeyUp(self, sender, keyCode, modifiers):

pass

def onKeyDown(self, sender, keyCode, modifiers):

pass

def onKeyPress(self, sender, keyCode, modifiers):

"""

This function handles the onKeyPress event, and will add the

item in the text box to the list when the user presses the

enter key. In the future, this method will also handle the

auto complete feature.

"""

if keyCode == KeyboardListener.KEY_ENTER and \

sender == self.todoTextBox:

id = self.remote.addTask(sender.getText(),self)

sender.setText("")

if id<0:

RootPanel().add(HTML("Server Error or Invalid Response"))

def onClick(self, sender):

id = self.remote.deleteTask(

sender.getValue(sender.getSelectedIndex()),self)

if id<0:

RootPanel().add(

HTML("Server Error or Invalid Response"))

def onRemoteResponse(self, response, request_info):

self.todoList.clear()

for task in response:

self.todoList.addItem(task[0])

self.todoList.setValue(self.todoList.getItemCount()-1,

task[1])

def onRemoteError(self, code, message, request_info):

self.Status.setText("Server Error or Invalid Response: " \

+ "ERROR " + code + " - " + message)

class DataService(JSONProxy):

def __init__(self):

JSONProxy.__init__(self, "../../default/call/jsonrpc",

["getTasks", "addTask","deleteTask"])

if __name__ == '__main__':

app = TodoApp()

app.onModuleLoad()

六つ目に、アプリケーションを実行する前にPyjamasを起動します:

Sixth, run Pyjamas before serving the application:

1.

2.

cd /path/to/todo/static/

python /python/pyjamas-0.5p1/bin/pyjsbuild TodoApp.py

これによりPythonコードがJavaScriptに変換されるためブラウザ上で実行できるようになります。

This will translate the Python code into JavaScript so that it can be executed in the browser.

アプリケーションは以下のURLより接続します:

To access this application, visit the URL:

http://127.0.0.1:8000/todo/default/todoApp

この小節はCasson Leighton(Pyjamas開発者)と共にChris Pironsが作成し、Alexei Vinidiktovが更新しました。Pyjamasのバージョン0.5p1にてテストされています。このサンプルはDjangoの参照74を参考にしています。

This subsection was created by Chris Prinos with help from Luke Kenneth Casson Leighton (creators of Pyjamas), updated by Alexei Vinidiktov. It has been tested with Pyjamas 0.5p1. The example was inspired by this Django page in ref.74.

AMFRPC

PyAMF

Adobe Flash

AMFRPCはフラッシュクライアントがサーバーと通信するために使用されているリモートプロシージャコールプロトコルです。web2pyはAMFRPCをサポートしていますが、PyAMFライブラリ導入済みのweb2pyをソースから実行する必要があります。LinuxやWindowsのシェルからこのコマンドを打てばインストールできます:

AMFRPC is the Remote Procedure Call protocol used by Flash clients to communicate with a server. web2py supports AMFRPC, but it requires that you run web2py from source and that you preinstall the PyAMF library. This can be installed from the Linux or Windows shell by typing:

1.

easy_install pyamf

(詳細ついてはPyAMFドキュメントを参考にしてください)

(please consult the PyAMF documentation for more details).

この小節では読者が既にActionScript言語をよく理解している前提で進めます。

In this subsection we assume that you are already familiar with ActionScript programming.

2つの数字の値を引数とし、それらを足し算し、集計結果を返すシンプルなサービスを作成します。このweb2pyアプリケーションの名称を"pyamf_test"とし、addNumbersを実行します。

We will create a simple service that takes two numerical values, adds them together, and returns the sum. We will call our web2py application "pyamf_test", and we will call the serviceaddNumbers.

まず始めに、Adobe Flash(MX2004以降のいずれかのバージョン)を利用して、Flash FLA ファイルの新規作成からフラッシュクライアントアプリケーションを作成します。ファイルの最初のフレームに、以下のコードを記述します。

First, using Adobe Flash (any version starting from MX 2004), create the Flash client application by starting with a new Flash FLA file. In the first frame of the file, add these lines:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

import mx.remoting.Service;

import mx.rpc.RelayResponder;

import mx.rpc.FaultEvent;

import mx.rpc.ResultEvent;

import mx.remoting.PendingCall;

var val1 = 23;

var val2 = 86;

service = new Service(

"http://127.0.0.1:8000/pyamf_test/default/call/amfrpc3",

null, "mydomain", null, null);

var pc:PendingCall = service.addNumbers(val1, val2);

pc.responder = new RelayResponder(this, "onResult", "onFault");

function onResult(re:ResultEvent):Void {

trace("Result : " + re.result);

txt_result.text = re.result;

}

function onFault(fault:FaultEvent):Void {

trace("Fault: " + fault.fault.faultstring);

}

stop();

このコードでフラッシュクライアントは "/pyamf_test/default/gateway"ファイルにある"addNumbersという関数に対応するサービスに接続することができます。フラッシュのリモート処理を有効にするためにActionScriptバージョン2 MXをインポートしておく必要があります。Adobe Flash IDEのクラスパス設定にこれらのクラスのパスを追加しておくか、単純に新規作成ファイルに"mx"フォルダを追加してください。

This code allows the Flash client to connect to a service that corresponds to a function called "addNumbers" in the file "/pyamf_test/default/gateway". You must also import ActionScript version 2 MX remoting classes to enable Remoting in Flash. Add the path to these classes to the classpath settings in the Adobe Flash IDE, or just place the "mx" folder next to the newly created file.

サービスコンストラクタの引数に注意してください。最初の引数はこれから作成するサービスに対応するURLです。三つ目の引数はサービスのドメイン名です。"mydomain"と呼ぶことにします。

Notice the arguments of the Service constructor. The first argument is the URL corresponding to the service that we want will create. The third argument is the domain of the service. We choose to call this domain "mydomain".

二つ目に、"txt_result"というダイナミックフィールドを作成してステージ上に配置します。

Second, create a dynamic text field called "txt_result" and place it on the stage.

三つ目に、上記のフラッシュクライアントと通信できるweb2pyゲートウェイをセットアップします。

Third, you need to set up a web2py gateway that can communicate with the Flash client defined above.

新しいサービスとフラッシュクライアント用のAMFゲートウェイをホストするpyamf_testという新規のweb2pyアプリケーションを作成します。"default.py"コントローラーを以下のように編集してください。

Proceed by creating a new web2py app called pyamf_test that will host the new service and the AMF gateway for the flash client. Edit the "default.py" controller and make sure it contains

1.

2.

3.

4.

5.

@service.amfrpc3('mydomain')

def addNumbers(val1, val2):

return val1 + val2

def call(): return service()

四つ目に、フラッシュアプリケーションをコンパイルしてpyamf_test.swfという名称でエクスポート/パブリッシュし、"pyamf_test"という新規に作成したゲートウェイをホスティングするアプライアンスの中の"static"フォルダに"pyamf_test.amf"、 "pyamf_test.html"、 "AC_RunActiveContent.js"、"crossdomain.xml"を入れます。

Fourth, compile and export/publish the SWF flash client as pyamf_test.swf, place the "pyamf_test.amf", "pyamf_test.html", "AC_RunActiveContent.js", and "crossdomain.xml" files in the "static" folder of the newly created appliance that is hosting the gateway, "pyamf_test".

以下のURLでテストできます:

You can now test the client by visiting:

http://127.0.0.1:8000/pyamf_test/static/pyamf_test.html

ゲートウェイはクライアントがaddNumbersに接続した際にバックグラウンドで実行されます。

The gateway is called in the background when the client connects to addNumbers.

もしAMF3の代わりにAMF0を使用している場合は:

If you are suing AMF0 instead of AMF3 you can also use the decorator:

1.

@service.amfrpc

の代わりに:

instead of:

1.

@service.amfrpc3('mydomain')

この場合はサービスURLも変更する必要があります:

In this case you also need to change the service URL to:

http://127.0.0.1:8000/pyamf_test/default/call/amfrpc

SOAP

SOAP

web2pyにはMariano Reingartが作成したSOAPクライアントとサーバーがあります。XML-RPCとほとんど同じように使うことができます:

web2py includes a SOAP client and server created by Mariano Reingart. It can be used very much like XML-RPC:

次のコードを考えてみましょう、例えば"default.py"コントローラーにて:

Consider the following code, for example, in the "default.py" controller:

1.

2.

3.

@service.soap('MyAdd',returns={'result':int},args={'a':int,'b':int,})

def add(a,b):

return a+b

そしてpythonシェルで以下を実行できます:

Now in a python shell you can do:

1.

2.

3.

4.

>>> from gluon.contrib.pysimplesoap.client import SoapClient

>>> client = SoapClient(wsdl="http://localhost:8000/app/default/call/soap?WSDL")

>>> print client.MyAdd(a=1,b=2)

{'result': 3}

テキストの値を取得する際に適切なエンコーディングをするには、u'proper utf8 text'を指定してください。

To get proper encoding when returning a text values, specify string as u'proper utf8 text'.

そのサービスのWSDLは:

You can obtain the WSDL for the service at

http://127.0.0.1:8000/app/default/call/soap?WSDL

そして公開されているメソッドのドキュメントを取得するには:

And you can obtain documentation for any of the exposed methods:

http://127.0.0.1:8000/app/default/call/soap