Fork me on GitHub

2009/08/17

はてな
ずいぶん前からXMLRPCをC++で扱うのに小さいライブラリないかなーと思ってました。


書いたといっても結構前からあったのですが、いらん所を削ぎ落として簡素なXMLRPCライブラリとして仕立て上げました。
簡素とは言えど、一般的な物ならば色々動きます。
例えば、ブログに一気に、しかも機械的にポストしたい場合とか

おれは今すぐXMLRPCしたいんだ!そしてどうしてもC++でXMLRPCしたいんだー!

って事ないですか?MovablebTypeをインストールしたけど、XMLRPCするのにC++コンパイラは入ってるけど、PerlやRubyやPythonは入ってない...とか悲しすぎます。
今回紹介する"tinyxmlrpc"はそんな、小さい様で大きな問題を解決出来るかもしれないライブラリです。
mattn's tinyxmlrpc at master - GitHub

tiny xmlrpc library written in C++

http://github.com/mattn/tinyxmlrpc/tree/master
今githubが不調でpushが反映されていません。リポジトリからのcloneは出来る様です。
ソースはC++で書いてます。WindowsとUNIX(Linuxでだけ動作確認)で動作します。

機能としては、STLのvectorとかmapとかに親和性高くなる様になっています。内部ではlibxmlでパースしていてcurlで通信しています。
小ささをウリにしていますが、libxmlとcurlのリンクは必須です。
サンプルコードを以下に示します。
#include "tinyxmlrpc.h"
#include <iostream>

class vargs {
private:
    tinyxmlrpc::value::Array args_;
public:
    vargs& operator <<(tinyxmlrpc::value arg) {
        args_.push_back(arg);
        return *this;
    }
    tinyxmlrpc::value::Array& list() {
        return this->args_;
    }
    vargs& first() {
        this->args_.clear();
        return *this;
    }
};

int main(int argc, char* argv[]) {
#ifdef _WIN32
    WSAData wsadata;
    WSAStartup(MAKEWORD(2,0), &wsadata);
#endif

    if (argc != 4) return -1;

    std::string endpoint = argv[1];
    std::string user = argv[2];
    std::string pass = argv[3];

    try {
        tinyxmlrpc::value::Array req;
        tinyxmlrpc::value res;
        tinyxmlrpc::value::Struct entry;
        vargs args;

        res = tinyxmlrpc::call(endpoint,
                "metaWeblog.getRecentPosts",
                (args.first() << "1" << user << pass << 3 << true).list());
        if (!tinyxmlrpc::failed(res)) {
            for(int n = 0; n < res.size(); n++) {
                std::vector<std::string> members = res[n].listMembers();
                std::vector<std::string>::const_iterator it;
                std::cout << "{" << std::endl;
                for(it = members.begin(); it != members.end(); it++) {
                    std::string val = res[n][*it].to_str();
                    std::cout << "  " << it->c_str() << "=" << val.c_str() << std::endl;
                }
                std::cout << "}" << std::endl;
            }
        } else {
            std::cerr << res << std::endl;
        }

        entry["title"] = "hasegawaにいじめられた";
        entry["description"] = "今日は、wassrでhasegawaにいじめられた。悲しかった。";
        entry["dateCreated"] = "";
        res = tinyxmlrpc::call(
                endpoint,
                "metaWeblog.newPost",
                (args.first() << "1" << user << pass << entry << 1).list());
        if (!tinyxmlrpc::failed(res)) {
            std::cout << "result:" << res << std::endl;
        } else 
            std::cerr << res << std::endl;
    } catch(tinyxmlrpc::value::Exception& e) {
        std::cerr << e.message << std::endl;
    }

    return 0;
}

これを使ってコマンドラインから引数として、MetaWeblogをサポートしているブログのエンドポイントURLユーザパスワードを指定すると、「hasegawaにいじめられた」というエントリがポストされます。
タイトルや本文に、特に意味はありません。すごいですね!


ライセンスはまだ決めてませんが、今のところBSDを予定しています。使ってみて下さい。
コードはgithubにあるので、patchウェルカムです。
今後は、この記事の為だけに仕上げた疑いのある汚いコードを綺麗にしていくつもりです。

参考文献:C++で軽量Webサーバ書いた。
Posted at 10:40 in ソフトウェア::lang::c | WriteBacks (1)
Tagged as: c++, xmlrpc
Bookmarks: このエントリーのtweets add to hatena add to hatena | add to delicious.com | add to livedoor.clip add to livedoor.clip | add to buzzurl add to buzzurl | add to fc2bookmark add to fc2bookmark | add to Yahoo Bookmark add to Yahoo Bookmark | add to Pookmark add to Pookmark

2008/04/10

はてな
pythonにはxmlrpclibがあり、常駐型のSimpleXMLRPCServerやCGIから使えるCGIXMLRPCRequestHandlerというとても有用なモジュールが存在します。
たとえば
def plus(num1, num2):
    return num1 + num2
という関数をXMLRPCサーバから公開したい場合
from SimpleXMLRPCServer import CGIXMLRPCRequestHandler
handler = CGIXMLRPCRequestHandler()
handler.register_function(plus)
handler.handle_request()
とすれば出来上がります。
またモジュールとしてインタフェースを提供したい場合は
class Foo:
    def plus(self, num1, num2):
        return num1 + num2
というインタフェースに対して
from SimpleXMLRPCServer import CGIXMLRPCRequestHandler
handler = CGIXMLRPCRequestHandler()
handler.register_instance(Foo())
handler.handle_request()
こうすれば出来上がり。
先ほど、NabeAtzz APIをXMLRPCとして呼び出せるインタフェースを作りました。
エントリポイントは
http://mattn.appspot.com/nabeatzz_xmlrpc
となります。試しにpythonでXMLRPC Clientを作成し呼び出してみます。
# -*- coding: utf-8 -*-
import sys
from xmlrpclib import ServerProxy

api = ServerProxy("http://mattn.appspot.com/nabeatzz_xmlrpc")
for n in range(1, 11):
  res = api.nabeatzz(n)
  print "%s : %s" % (res['number'], res['japanese'])
  if res.has_key('nabeatzz'):
    for c in res['nabeatzz']:
      print "  %s" % c
コードはこんな感じでしょうか。これを実行すると正しく
1 : いち
2 : に
3 : さん
  アホになる
4 : よん
5 : ご
  犬っぽくなる
6 : ろく
  アホになる
7 : なな
8 : はち
9 : きゅう
  アホになる
10 : じゅう
  犬っぽくなる
という結果が得られました。

2007/11/01

はてな
XMLRPC(XML Remote Procedure Call)は、HTTPを介してXMLを扱い透過的にサーバ側のメソッドを実行するAPI実装です。

最近のミニブログ界ではJaikuがGoogleに買収されてからJaikuに人が入り始め、賑わってから少し経ちます。ミニブログで代表的なTwitterとJaikuは一見同じミニブログに見えて、APIとしては違うものを採用しています。

TwitterのAPIは基本的にRESTとBasic認証および出力フォーマット指定というAPIを採用しており、クライアントを作成する開発者はURLに対してGET/POST出来るライブラリと、XML/JSON/RSS/ATOMの中から自分の使いたいフォーマットを処理出来るライブラリを選ぶ事が出来ます。
またJaikuのAPIはBasic認証ではなくpersonal_keyと呼ばれるAPIKEYとユーザ名で認証します。取得系はTwitterとそれ程変わりませんが更新系は以前にも書いた通りJSONもしくはXMLRPCに限定されてしまっています。つまりJSONで更新したらJSONのレスポンスが、XMLPRCで更新したらXMLRPCのレスポンスが返されます。JSONが扱いにくい言語を使用する場合にはXMLRPCを強制されてしまう事になります。

さて今日はJaikuのAPIに限った話ではなく、Jaikuを使ってXMLRPCのクライアント実装と、XMLRPCサーバについての話をしたいと思います。
まず送受信されるデータについて。

XMLRPCは、決められたXML構成にメソッド名および構造化可能なパラメータをリクエストとして送信し、同じく構造化可能なレスポンスを受け取るXML送受信APIです。
リクエストフォーマットは以下の様な記述になります。
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>メソッド名</methodName>
  <params>
    <param><value><string>パラメータ1</string></value></param>
    <param><value><string>パラメータ2</string></value></param>
  </params>
</methodCall>
またレスポンスフォーマットは以下の様になります。
<?xml version="1.0" encoding="utf-8"?>
<methodResponse>
  <params>
    <param>
      <value>
        <string>結果</string>
      </value>
    </param>
  </params>
</methodResponse>
リクエストパラメータは「params」というノードを括られており、配列になっているのが分かるかと思います。また値が格納される「value」ノードには「<string>」や「<int>」といった、その値の型が定義されています。
上で書いた通り、リクエストパラメータとレスポンスは構造化出来ますので、例えばレスポンスで構造体を表すならば
<?xml version="1.0" encoding="utf-8"?>
<methodResponse>
  <params>
    <param>
      <value>
        <struct>
          <member>
            <name>メンバ1</name>
            <value><int>157</int></value>
          </member>
          <member>
            <name>メンバ2</name>
            <value><string>Sample</string></value>
          </member>
        </struct>
      </value>
    </param>
  </params>
</methodResponse>
と書く事が出来ます。
詳しくはXMLRPCのオフィシャルサイトで確認出来ます。またオフィシャルサイトではXMLRPCを使った処理系毎の実装一覧XMLRPCが使えるサービス一覧も確認出来ます。

ところで、Windows Live WriterBlogWriteubicast Blogger等といったブログ投稿ツールはXMLRPCを使用してブログ記事を操作しています。それらAPIは以下の様に分類されています。
  • Blogger API: Bloggerが提供してるAPI
  • metaWeblog API: Blogger APIに足らない部分を補足する形で登場したAPI
  • MovableType API: Movable Typeが提供してるAPI
  • LiveJournal API: Bloggerが提供してる独自API
一般的に汎用的なブログ投稿ツールでは上記の内、上3点を標準実装しています。
さてJaikuの場合、XMLRPCのインタフェースは一つしか公開されておらず(2007/11/01時点)、そのスペックは
presence.send({
    user => 'your username as string',
    personal_key => 'your personal key as string',
    message => 'message as string',
    optional icon => 'icon number as int',
    optional location => 'location as string',
    optional generated => 'generat flag as boolean'
})
こんなイメージとして表現出来ます。
簡単な物をPerlで書くならば

jaiku.pl
#!/usr/bin/perl
use strict;
use Encode qw(from_to);
use XMLRPC::Lite;
use Data::Dumper;

my ($user,$personal_key,$message,$icon) = @ARGV;
from_to($message, "shiftjis", "utf8") if $^O eq "MSWin32";

print Dumper(XMLRPC::Lite
    ->proxy('http://api.jaiku.com/xmlrpc')
    ->call('presence.send', {
            user => $user,
            personal_key => $personal_key,
            message => $message,
            icon => ($icon || 300),
        })->result);
と書く事が出来ますね。これをコマンドラインから
./jaiku.pl username xxxxxxx 本日は晴天なり
※「xxxxxxx」はpersonal_keyです。APIドキュメントの右下あたりに記述されています。
と実行すればJaikuのステータスが更新されます。例では最終引数に省略可能なアイコン番号を付けていますので
./jaiku.pl username xxxxxxx 曇り 398
と実行すれば
jaiku-icon-cloudy
といった感じにアイコンを付ける事が出来ます。
なお、Jaikuのアイコン番号に対する実際のアイコン対応表を作りました。
JavaScriptをOnにして頂いて、以下のリンクをクリックして下さい。
アイコンリストを表示
さて話をXMLRPCに戻して、Perlでは「XMLRPC::Transport::HTTP」というXMLRPCを透過的にPerlのパッケージおよびサブルーチンとバインドしてくれるモジュールがあり、XMLRPCサーバを簡単に実装する事が出来ます。
例えば数値パラメータ2つ貰い足した結果を返すXMLRPCサーバならば以下の様に実装出来ます。
#!/usr/bin/perl

use strict;
use XMLRPC::Transport::HTTP;

package calc;
sub plus {
    shift if UNIVERSAL::isa( $_[0] => __PACKAGE__ );
    my $lhs = shift;
    my $rhs = shift;
    my $res = $lhs + $rhs;
    return {result=>SOAP::Data->type('int' => $res)};
}

XMLRPC::Transport::HTTP::CGI
    ->dispatch_to('calc')->handle;
モジュール名がpackage「calc」に、メソッド名がサブルーチン「plus」にバインドされている事が分かるかと思います。
これを実行するには
#!/usr/bin/perl
use strict;
use warnings;
use Encode qw(from_to);
use XMLRPC::Lite;
use Data::Dumper;

my $url = 'http://example.com/xmlrpc/calc.cgi';
warn Dumper(XMLRPC::Lite
    ->proxy($url)
    ->call('calc.plus', 1, 2)->result);
というクライアント側のコードで実行出来ます。
勿論、XMLRPCを扱うならばクライアント/サーバはPerlでなくても良い訳です。pythonで実装するならば
from xmlrpclib import ServerProxy
proxy = ServerProxy("http://example.com/xmlrpc/calc.cgi")
print proxy.calc.plus(1, 2)
という実装になります。
XMLRPCは既に色んな言語で実装されており、それぞれに使い方は異なりますが扱う仕様が決められている為に、移植に戸惑う事はそれ程ないかと思います。
※特にpythonに関してはメソッドバインディングされますので、かなり透過的に扱えます。

例を元にXMLRPCを説明して来ましたがどうでしたでしょうか?
色んな言語でJaikuクライアントを実装してみるのも面白いかもしれませんね。

Posted at 14:00 in ソフトウェア | WriteBacks (0)
Tagged as: xmlrpc
Bookmarks: このエントリーのtweets add to hatena add to hatena | add to delicious.com | add to livedoor.clip add to livedoor.clip | add to buzzurl add to buzzurl | add to fc2bookmark add to fc2bookmark | add to Yahoo Bookmark add to Yahoo Bookmark | add to Pookmark add to Pookmark