2009年8月27日木曜日

rome vs abdera

別に対決と言う訳ではないのですが、AtomPubを実現するに当たってこの両者を比べることが多かったので。その違いをまとめておきます。

  1. 対応 JDK versionの違い
  2. atomDateConstruct の表現
  3. atomCategory 要素
  4. atomEntry 解析をどうするか。
1番は書いた通りでそれぞれのサポートするJDKのversionが異なります。romeは JDK1.4以上,
abderaは JDK1.5(正式にはJDK5か)以上となっています。今回作業した環境ではAtomPubクライアント側にはJDK1.4
しか準備出来ていない影響でやむを得ず romeを使いました。

2番はatomPublishedや、atomUpdated部分の日付の表現です。これはromeがミリ秒を表現できないようでabdera(AtomPub サーバー)からfeedしたデータはもれなくミリ秒がクリアされてしまいます。どうしたもんかと思いましたが、本来の仕様から
いうと、ミリ秒の表記はいらないみたいですね。(でもどこかで時間表現は正確にやるべきだという記事を読んだ記憶が・・・)

3番はromeがlabel属性をサポートしてない。これだけです。ちょっと困りました。ただしこちらもAtomフォーマットとしては
必須となっていないので文句は言えないのですが。

4番はromeをfeeder提供側ではなく、AtomPubクライアントとして利用する場合の致命的な問題点(ただし、romeはAtomPubをサポートしている訳でないので責められないけど)なのですが、romeではfeedアグリゲータの役割として(rome-fetcher)が
が提供されているのでこいつを利用してなんとかごまかします。

Entryとして出力する場合には一旦romeに任せてFeed を作成した後、XML Documentとして扱いentryを取得して
Writerにシリアライズ(出力)してあげます。

public void output(SyndEntry entry, Writer writer, boolean pretty) throws Exception {
SyndFeed feed = new SyndFeedImpl();
feed.setEntries(Arrays.asList(new Object[]{ entry }));
feed.setFeedType("atom_1.0");
SyndFeedOutput out = new SyndFeedOutput();
try {
Document document = out.outputW3CDom(feed);
Element element = getEntryElement(document);
OutputFormat formatter = new OutputFormat(document, "UTF-8", pretty);
XMLSerializer serializer = new XMLSerializer(writer, formatter);
serializer.serialize(element);
} catch (FeedException e) {
throw e;
} catch (IOException e) {
throw e;
}
}

private Element getEntryElement(Document document) {
NodeList list = document.getElementsByTagName("entry");
Element element = (Element) list.item(0);

/* Documentから namespace 属性をコピー */
NamedNodeMap map = document.getFirstChild().getAttributes();
for (int i = 0; i < map.getLength(); i++) {
Node node = map.item(i);
if (logger.isDebugEnabled()) {
logger.debug("node name: {}", node.getNodeName());
logger.debug("node value: {}", node.getNodeValue());
}
element.setAttribute(node.getNodeName(), node.getNodeValue());
}
return element;
}


逆にEntryを受け取る際には、InputStreamから受け取ったEntryを通常のXML Documentとして取得し(登場するbuilderは
DocumentBuilderFactory.newInstance()から作った通常のもの)、一方で新たにFeed Documentを作成し(getFeedRootメソッド)
その作成したFeedにEntryをimportNodeしてダミーのFeedを作成後、romeのライブラリで解析しSyndEntryとして改めて取得
します。うーん、言葉にすると結構面倒。

public SyndEntry input(InputStream input) throws Exception {
Document document = null;
try {
document = builder.parse(input);
} catch (SAXException e) {
throw e;
} catch (IOException e) {
throw e;
}
if (document == null)
throw new IllegalStateException("docuent is null!");
return build(document);
}

/**
* DocumentからEntryを取得します。
* @param document
* @return
*/
public SyndEntry build(Document document) throws Exception {
SyndEntry entry = null;
try {
Document doc = editRootElement(document);
WireFeedInput wireFeed = new WireFeedInput(false);
SyndFeed feed = new SyndFeedImpl(wireFeed.build(doc), false);
List entries = feed.getEntries();
if (entries.size() >= 1)
entry = (SyndEntry) entries.get(0);
else
logger.warn("feed に含まれる entry が {}個です。", new Integer(entries.size()));

} catch (IllegalArgumentException e) {
throw e;
} catch (FeedException e) {
throw e;
}
return entry;
}

/* getFeedRoot */
private Document getFeedRoot(Document root) {
Element element = root.createElement("feed");
Element id = root.createElement("id");
Element author = root.createElement("author");
author.appendChild(root.createElement("name"));
Element title = root.createElement("title");
title.appendChild(root.createTextNode("dummy title"));
Element updated = root.createElement("updated");
updated.appendChild(root.createTextNode(
DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(new Date())));
id.appendChild(root.createTextNode("urn:uuid:dummyid"));
element.setAttribute("xmlns", "http://www.w3.org/2005/Atom");
element.setAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
element.appendChild(id);
element.appendChild(title);
element.appendChild(author);
element.appendChild(updated);
root.appendChild(element);
return root;
}

/* editRootElement */
private Document editRootElement(Document document) {
Document root = getFeedRoot(builder.newDocument());
Node node = root.importNode(document.getElementsByTagName("entry").item(0), true);
Element element = root.getDocumentElement();
element.appendChild(node);
return root;
}

0 件のコメント: