» Permanent link
|
|
はてなの技術が今後目指す方向を読んだ。こういう方向性を明確に打ち出せるのがうらやましい。それに比べて自分のやっていることはなんとつまらなくて淋しいことか、と溜息が出てしまうのだった。
こちらの日記のコメントにも書いたけど、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");
}
}
ラベル: atompub, java