yohei-y:weblog

XML と REST/Web サービス関連の話題が中心の weblog です

2005-05-25

Alpine

前のエントリの続き。

同論文では Alpine の設計目標が提示されている。

  1. Stay in the XML space as much as possible.
  2. Take advantage of as much leading edge infrastructure as we can.
  3. Adopt the the handler chain pattern of Axis/JAX-RPC.
  4. Target SOAP 1.2 (POST only, WS-I Basic Profile 1.1)
  5. Document/literal mssages only, not RPC/encoded.
  6. Support XSD and Relax NG schemas.
  7. Run server-side, client-side, and as an intermediary.
  8. No support for JAX-RPC or JAX-M/SAAJ APIs.
  9. Configurable procedurally, through the Java Manage- ment API (JMX).
  10. Permit dynamic handler chain configuration during mes- sage processing.
  11. One supported parser.
  12. Run on Java 1.5 and later.
  13. No provision of side features such as a built in HTTP server, or a declarative configuration mechanism. These are delegated to other products.

この中でも注目は 1 と 3 だ。

XML の API として、彼らは XOM を念頭においているようだ。 ハンドラチェーンを使うのは「知っている XML だけを処理する」モジュールを接続して処理をするためだろう。

いやーしかし、こういう論文が Sun でも IBM でも BEA でもなく HP から出てくるところが、面白いなー。

ラベル: ,

Java SOAP スタックの再考

とても興味深い論文を読んだ。 Steve Loughran と Edmund Smith の "Rethinking the Java SOAP Stack" だ。 この論文は現在の Java の SOAP 実装スタック、特に JAX-RPC の問題点を XML セントリックな視点から明確に指摘している。 また、最後の部分で Alpine という新しい SOAP スタックのコンセプトを提案している。

Alpine の方はまだどんなものかわからないので、これからに期待したいが、 JAX-RPC の問題点の方はとても面白い。 雰囲気を伝えるために論文の第二章 "基本的な JAX-RPC の欠陥(The Fundamental Flaws of JAX-RPC)" の見出しを日本語で引用する。

  1. オブジェクト/XML のインピーダンスミスマッチ
    1. XML 要素の Java クラスへのバインディング
    2. XML の名前(Names)の Java 識別子へのマッピング
    3. Enumeration
    4. ポータブルでない型
    5. オブジェクトのグラフの直列化
    6. XML メタデータと名前空間
    7. メッセージ妥当性検証
    8. XML と直列化データの不適切な混和
    9. フォールト処理
  2. SOAP は RPC ではない
  3. SOAP は RMI ではない
  4. WSDL: 別の複雑さ

このように論文で指摘されている問題点はいくつもあるのだが、基本的には SOAP/XML Web サービスを Java 世代のプログラミング言語(Java に加えて C#, VB.NET も)にネイティブにマッピングしてしまうことへの批判となっている。 その中でも特に大きいのが XML と オブジェクトのデータモデルの違いに起因するインピーダンスミスマッチだ。 ここで指摘されている各事項は、XML をオブジェクトにマッピングしたり、 オブジェクトを XML にマッピングしたりすることが本質的に難しいことを示している。

上でも述べたとおり、この論文は XML セントリックな視点から記述されているので、 逆に RPC/RMI/Object セントリックな視点から見るとまったく許容できない内容なのかもしれない。 そういう意味で面白い文(Don Box も引用している)がある。

我々は Web サービス開発者にはただ二つの分類があると考える。 XML に満足していてそれを扱う仕事をしたいと願っている人々と、満足していないのに結局はなんだかんだいってそれを扱う人々だ。

(We believe that only two categories of web service developer exist: those who are comfortable with XML and want to work with it, and those who aren’t but end up doing so anyway.)

明らかに僕は前者だ :-)

ラベル: , ,

2005-04-05

Java からはてなフォトライフAtomAPIを使う

はてなの技術が今後目指す方向を読んだ。こういう方向性を明確に打ち出せるのがうらやましい。それに比べて自分のやっていることはなんとつまらなくて淋しいことか、と溜息が出てしまうのだった。

こちらの日記のコメントにも書いたけど、REST だの SOA だと騒いでるのは、ごくごく一部の(ちょっとだけ影響力のある)人たちだけで、ニュートラルな立場の人々はそんなことは関係なく次々とウェブサービスで面白いハックを送り出しているのだ。そして現在は、ニュートラルな側(google, amazon, hatena, etc...)が影響力を持つ時代になっている。かつてハードウェアベンダからマイクロソフトに影響力が移ったのと同じように。

僕はもうすぐ30になるけれど(ああ、ついに20代ともお別れか)次の10年を考えなければいけなさそうだ。僕はアーキテクチャの流儀(architectural style)としての REST には重要性があると思っているし、SOA にだって見るべきところがないわけではないと思っている。どちらも学問と現場の間で言えば学問よりの中間層となるだろう。アーキテクチャの流儀ってソフトウェア工学の言葉だしね。でもそれらを仕事を通じて実利を生み出せているのか不安にもなる。もちろん仕事の価値はそれだけではないが、僕にとってのエンジニアとしてのやりがいはそこなのだ。その学問と現場の間をつなぐような仕事がしたい、ということか。そういえば檜山さんのこの文書にも深く共感したのだった。

と、ここまではグチです。

本題は、はてなウェブサービスを Java から使うために必用なX-WSSE ヘッダを扱うライブラリがうまくみつけられなかったので(本当はws-fx の wss4j が妥当だろう)、自分で書いてしまった、というハナシです。といっても、吉松さんの C# 版のコードを参考に Java に移植しただけ…。commons httpclient と commons codec が必要です。言うまでもないけど、僕のパスワードはうそなので、そのまま使っても動かないはず。コメントないけど自由にコピペして使ってください。

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;


public class AtomTest {
  
  private HttpClient client;
  
  public AtomTest() {
    this.client = new HttpClient();
  }
  
  public void get(String url, String username, String password)
    throws HttpException, IOException {
    GetMethod get = new GetMethod(url);
    get.addRequestHeader("X-WSSE", getWsseHeaderValue(username, password));
    this.client.executeMethod(get);
    System.out.println(get.getStatusLine().toString());
    System.out.println(get.getResponseBodyAsString());
  }
  
  protected final String getWsseHeaderValue(String username, String password) {
    try {
      byte[] nonceB = new byte[8];
      SecureRandom.getInstance("SHA1PRNG").nextBytes(nonceB);

      SimpleDateFormat zulu = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
      zulu.setTimeZone(TimeZone.getTimeZone("GMT"));
      Calendar now = Calendar.getInstance();
      now.setTimeInMillis(System.currentTimeMillis());
      String created = zulu.format(now.getTime());
      byte[] createdB = created.getBytes("utf-8");
      byte[] passwordB = password.getBytes("utf-8");
    
      byte[] v = new byte[nonceB.length + createdB.length + passwordB.length];
      System.arraycopy(nonceB, 0, v, 0, nonceB.length);
      System.arraycopy(createdB, 0, v, nonceB.length, createdB.length);
      System.arraycopy(passwordB, 0, v, nonceB.length + createdB.length,
                       passwordB.length);

      MessageDigest md = MessageDigest.getInstance("SHA1");
      md.update(v);
      byte[] digest = md.digest();

      StringBuffer buf = new StringBuffer();
      buf.append("UsernameToken Username=\"");
      buf.append(username);
      buf.append("\", PasswordDigest=\"");
      buf.append(new String(Base64.encodeBase64(digest)));
      buf.append("\", Nonce=\"");
      buf.append(new String(Base64.encodeBase64(nonceB)));
      buf.append("\", Created=\"");
      buf.append(created);
      buf.append('"');
      return buf.toString();
    } catch (NoSuchAlgorithmException e) {
      throw new RuntimeException(e);
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
  }

  public static void main(String[] args) throws Exception {
    AtomTest test = new AtomTest();
    test.get("http://f.hatena.ne.jp/atom", "yohei", "hoehoe");
  }
}

ラベル: ,