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;
}

2009年5月28日木曜日

jmeterで繰り返し作業

検索キーワードでjmeterからこのブログに来る人もあったので、間違いを修正。

WinRunnerで実施していた作業を訳あって、他のツールに乗り換える必要が発生しました。
その候補にJmeterがあったので調査したネタです。

ストーリー
  • 繰り返し実施する作業のパラメータをCSVで保持
  • 実施した結果画面の内容を一覧として別ファイル(CSV)に出力すること。
採用したネタ
  • CSV Data Set Config(パラーメータ読み込み)
  • 正規表現抽出
  • BeanShell(結果ファイル出力)
CSV Data Set ConfigはJmeterを負荷テストで利用する際にもよく利用されていると思います。

CVSRead:一行ごとにスレッドを割り当てる。ループ処理では同じスレッドは同一データのまま。
CSV Data Set Config:一行ごとにスレッドはデータを読み込む
という違いがあるようです。

ファイル内のデータをランダムに読み込んで実行することが出来るようですが、今回は先頭からEOFまで
実施すれば良かったので素直にCVS Data Set Configの機能を使います。

正規表現抽出は通常のアサーションでも設定する内容だと思うのでパス。

結構ハマったのがBeanShell。まず、jmeterには付属していないので別途ダウンロードします。
私はJMETER_HOME/lib/extに入れておきました。

通常のアサーション検証後にBeanShell Listenerとして登録したのですが、
これがjavaの構文そのままでscriptとして動くので起動方法さえ分かれば楽チンでした。
アサーションや、正規表現抽出で取得した値もvarsという変数からアクセス出来るようになってます。

例えば、正規表現で取得した${name}を扱う場合には
System.out.println("val is " + vars.get("name"));
こんな感じで出力が可能です。

scriptを確認したい場合には

java -cp bsh-X.X.jar bsh.Interpreter で
bsh% のプロンプトが出現、そのまま
java -jar bsh-X.X.jarで起動すればterminalが出現するので簡単に確認可能です。

ちなみに対象WEBアプリがShift_JISだったのですが、マルチバイト文字をリクエストする際のエンコードが
うまく行っていなかった
のでここもBeanShellを利用して

パーセントエンコーディングは HTTPリクエストのContent encodingを指定することと、Encode?のチェックボックス
をチェックすることで問題なく対応できました。(追記)
${__BeanShell(new org.apache.commons.codec.net.URLCodec("Shift_JIS").encode("${name}"))}


こんな感じで解決できました。
(jmeterにcommons-codecが付属しているのでダウンロードは不要です)

2009年5月15日金曜日

hudsonに乗り換えました。

継続的統合にmavenとの相性からcontinuumが良いと思っていたのですが、操作感の重さと日本語環境の貧弱さが
如何ともしがたくて、hudsonを試してみました。で、ソッコーで乗り換えました。

  1. 日本語環境で困る事なし。
  2. アプリ環境設定が楽チン。
  3. Ajaxで処理中のログの閲覧が快適。
既にjetty + continuumの環境があるのでwebapp-plusディレクトリにhudson.warを置いてjetty再起動するだけで
簡単に使えました。

以前はjettyのプロセスのためにjettyユーザーを作成していたのですが、continuum起動時に例外が発生するため
やむなく、root権限で起動させていたのですが、hudsonでは問題なくjettyユーザーで起動できたので、権限問題も
クリア出来ました。

定期的なビルドと共にリポジトリに更新があった場合にもビルドしてほしい訳ですが、hudsonでは提供されるREST API
を利用するようです。

apacheのリポジトリの構成をまねっこしている自分のリポジトリでは post-commit スクリプトで以下のように
ディレクトリ構成からプロジェクト名、ブランチを取得してREST APIにアクセスしています。

最後の「delay=30sec」はcommitするサイズが大きいとdelayが十分でなくなって2重でビルドが起動してしまうことが
あったので適当に30秒としています。

selinuxを適応している場合は chcon -t httpd_sys_script_exec_t post-commit でコンテキストの変更を忘れずに。


for ELEMENT in $(svnlook dirs-changed -r $REV /foo/var/repos); do
if [ "$PROJECT" = "" ]; then
PROJECT=$(echo $ELEMENT | cut -d/ -f1)
if [ $PROJECT = incubator ]; then
PROJECT=$(echo $ELEMENT | cut -d/ -f2)
fi
fi
if [ ! -z $(echo $ELEMENT | grep "branches") ]; then
BRANCH=$(echo $ELEMENT | cut -d/ -f3)
break;
fi
done

if [ "$PROJECT" = incubator ]; then
exit
fi
if [ "$BRANCH" != "" ]; then
TARGET="$PROJECT-$BRANCH"
else
TARGET="$PROJECT"
fi

wget -O /dev/null http://domain.jp/hudson/job/$TARGET/build?delay=30sec

2009年5月6日水曜日

abdera 1.0 release

abderaが0.4をリリースした後、ダンマリだったので心配になっていたのですが、そろそろ1.0としてリリースされるようです。
この開発者用のメーリングリストでは次の計画も議題にされていて

  1. Axiomいいんだけど、SOAP関連の依存ライブラリが付いてくるのでAxiom依存をやめよう。
  2. i18n(internationalization)のライブラリをAbdera配下のサブプロジェクトとして独立させよう。
  3. RSS機能を統合しよう。
  4. APIをもっと簡素化しよう。

まだ、決定ではないみたいですが、大体こんな事みたいです。

1番は自前で用意したくなるのはなんとなく理解できますね。

i18nのライブラリは確かに独立しているし、自分でもAbderaを利用したアプリを作ろうとした時にIRIをそのまま
取得して扱うとAbdera依存がすべてといっていいほどに発生してしまうので、取得時にURIに変換してました…
独立してくれるのは喜ばしいことだと思います。

RSS機能の統合も利用者としてはrome以外の選択肢、対抗として考えたときには有効ですよね。
基本的に要素は結構似てる部分があると思うのでAPIを簡素化とうまく協調して実現されることを期待したいです。

2009年4月28日火曜日

業務アプリ

よく日本の業務アプリは自社向けのカスタマイズ度が高いと聞く。

うちの会社でもグループウェアを導入しているのですが、
見た感じ、自分だけかも知れないけど使いこなしてるとはいえないような。

もっと業務ベッタリのアプリだったら利用されるんだろうけど、そのようなものは今まで
少なくとも「良し」とはされて来なかった気がする。

でもそれってソフトウェアを売る立場の意見?
「内製化」が求められる今なら、すこぶるよい題材なのではないのか?
と考えると色々とやってみる価値はありそうだ。

スキルアップ→新技術の適応→業務効率UP→スキルアップとなれば
外注→値段をたたく→不便なアプリ→業務効率UPしない→値段をたたく
という今までの負のサイクルから脱出出きるはずだ。

そもそも日本って国はカレーしかり、ラーメンしかり、一旦自分の懐まで持ってきて
いじくり回して自分のものにしてしまうのが得意のはず。

出来合いの汎用過ぎるアプリでもいい所は学んで、自分のものとしてしまおう。
利自即利他、身近な人の業務を楽にすることから始めよう。

ま、最初からうまく行くはずもないし。

2009年3月27日金曜日

wicket XPathModel

wicketではOGNL(Object Graph Navigation Language)を利用してPOJOでなくても単なるメンバー変数にもアクセス出来るので自分でHttpServletRequestからgetParameterしてsetFoo()などをしていたことが何だかバカらしく思えるくらいで、その煩雑さから開放されるだけでも素晴らしい。
(個人的にはCompoundPropertyModelまでいくと、ちょっとtoo muchな気がしないでもないのですが)

そうなってくると、余計な事を考える時間が出来てしまい、バックエンドにAtomPub(or RSS)をPOJO(Bean)に詰め替えずにXMLDocumentに対してOGNL式の部分をXPathで記述できたら幸せだなーと気がつきました。

実装はこんな感じ?

new Label("title", new XPathModel(XmlDocument, "node/title"));

いや、自分が気が付くくらいだから既に実装があるんじゃないか?と思ってググってみたら、既にwicketでは2005年に同じ話題があったのですね。このメールスレッドの結論では「aargh. Shouldn't be too hard to write such a model.」なんて言ってるし、最新のソースでも見当たらないようなので実装には至っていないようです。

まだ英語がよく読めていないのですが、確かに汎用的なModelとなると大変そうだけど、条件を限定すれば
(XMLデータサイズ、valid xml)行けそうな気がします。

あくまでも自作アプリ用レベルなんでしょうかね。

2009年3月8日日曜日

wicket勉強会に参加してきた。

個人的に勉強している事もあり、3/6(金) wicketの勉強会@Gree株式会社に行ってきた。
開始ちょっと前についたつもりだったけど既に机は埋まっており、仕方なく後ろの方の席に。

ちょくちょく気になるセミナーなどは参加してるけど、勉強会ということでは初めてだった。
個人的には聞くモード全開 + そこまでの知識もない状態だったので意図されていたような
質疑応答/議論には参加できなかったけど(あの人数だとちょっとつらい)、wicketの知らない
部分も知れてよかった。

一番気になったのは「デザイン指向」と「部品指向」の話で、そのまま仕事に反映してきそうな
内容だった。まだ自分で消化できていないけど、Panel化した部分的なHTMLをブラウザでデザイン
全体が確認できるようにするために、利用しない部分も管理(最新化?)していくのはやっぱり大変
大変だろうという気がする。それもPanelが増えれば増えるほど。

うーん、仕事で利用出来るようになるかは「勉強会」でも話題になったけど、View処理部分を
置き換えることになるだけに、まだまだハードルが高そうだ。

2009年2月17日火曜日

wicketと格闘 mountポジション (イントラ用語集5)

かなり昔に考えていたイントラ用語集のネタが腐る前に掘り起こすことにします。

http://domain.name/context/servlet/path/info.ext

REST的にリソースを考えた上、本来のURIの不透明性よりもリソースのコンテントを示すほうが有用と
考えたところまででした。

Servlet処理はそれなりに進んでいたものの、画面デザインは自身にセンスを持ち合わせていないこともあり、
悩んでいたのですが、JSPでガリガリやるのは画面表示が確認できないし、タグを駆使してもどうしてもJSPは綺麗
に書けないと思ったので(個人的に好きになれない…)、Next Strutsを探す旅と言う意味も込めてイロイロと探って
みた結果、HTMLをそのまま使えるという(POHPと言うらしい)wicketにたどり着きました。

wicketの感想はまたの機会にするとして、wicketでのURLの扱いはかなりユニークに出来ているみたいでフレームワーク
でありながら、拡張も簡単に出来る工夫がしてあるようです。

今回の目的である、リソースを表すURLには
  • 拡張子がない時はhtmlとして扱う
  • htmlを付けたURLでも同じように扱う
事を目的にBookmarkablePageRequestTargetUrlCodingStrategyを拡張した
MixedParamUrlCodingStrategy クラスをマネして参考に作成してみました。
wicket歴も浅いので、いい加減なところもありますが、ツッコミがありましたらお願いします。

コンストラクタ

public FileExtensionUrlCodingStrategy(String mountPath,
Class bookmarkablePageClass, String pageMapName,
String[] parameterNames, String extension) {
super(mountPath, bookmarkablePageClass, pageMapName);
/* TODO 拡張子からMimeを確認する方法がバラバラ
* Resource#detectContentType参照
* 下記はgeronimo-activationライブラリの拡張したものを流用
*/
if (!FileTypeMap.getDefaultFileTypeMap().exist(extension))
throw new IllegalArgumentException("指定した拡張子はサポートされていません。: " + extension );
this.parameterNames = getDesignedParameterNames(parameterNames);
this.extension = extension;
}


getDesignedParameterNamesメソッド

private String[] getDesignedParameterNames(String[] parameterNames) {
boolean found = false;
for (int i = 0; i < parameterNames.length; i++) {
if ("format".equals(parameterNames[i])) {
found = true;
break;
}
}
return found ? parameterNames : (String[]) ArrayUtils.add(parameterNames, "format");
}


appendParametersメソッド Redirect After Postでリダイレクトされる時のURLの生成
ほとんどMixedParamUrlCodingStrategyのマネ。

@SuppressWarnings("unchecked")
@Override
protected void appendParameters(AppendingStringBuffer url, Map parameters) {
if (!url.endsWith("/"))
url.append("/");

Set parameterNamesToAdd = new HashSet(parameters.keySet());
/* Find index of last specified parameter */
boolean foundParameter = false;
int lastSpecifiedParameter = parameterNames.length;
while (lastSpecifiedParameter != 0 && !foundParameter)
foundParameter = parameters.containsKey(parameterNames[--lastSpecifiedParameter]);

if (foundParameter) {
for (int i = 0; i <= lastSpecifiedParameter; i++) {
String parameterName = parameterNames[i];
if ("format".equals(parameterName))
continue;
if (i != 0 && !url.endsWith("/"))
url.append("/");
final Object param = parameters.get(parameterName);
String value = param instanceof String[] ? ((String[])param)[0] : (String)param;
if (value == null)
value = "";
url.append(urlEncodePathComponent(value));
parameterNamesToAdd.remove(parameterName);
}
/* format未指定の場合のみURLに追加
* defaultはhtmlになるが、URLとして表現しない。
* http://server/context/pathinfo
*/
if (parameters.get("format") != null)
url.append(".").append(parameters.get("format"));

parameterNamesToAdd.remove("format");
}

if (!parameterNamesToAdd.isEmpty()) {
boolean first = true;
final Iterator iterator = parameterNamesToAdd.iterator();
while (iterator.hasNext()) {
url.append(first ? '?' : '&');
String parameterName = (String)iterator.next();
final Object param = parameters.get(parameterName);
String value = param instanceof String[] ? ((String[])param)[0] : (String)param;
url.append(urlEncodeQueryComponent(parameterName)).append("=").
append(urlEncodeQueryComponent(value));
first = false;
}
}
}


Pageクラスに渡すPageParameter作成メソッド

@SuppressWarnings("unchecked")
@Override
protected ValueMap decodeParameters(String urlFragment, Map urlParameters) {
PageParameters params = new PageParameters();
/* Add all url parameters */
params.putAll(urlParameters);
String urlPath = urlFragment;
if (urlPath.startsWith("/"))
urlPath = urlPath.substring(1);

if (urlPath.length() > 0) {
String[] pathParts = extensionSplit(urlPath);
if (pathParts.length > parameterNames.length) {
throw new IllegalArgumentException(
"Too many path parts, please provide sufficient number of path parameter names");
}

for (int i = 0; i < pathParts.length; i++)
params.put(parameterNames[i], urlDecodePathComponent(pathParts[i]));
} else {
/* "/"指定時にはディレクトリインデックス "index.html"をマッピングする。
* TODO 将来的にはweb.xml#welcom file listから取得出来るようにしたい。
*/
params.put(parameterNames[0], "index");
params.put(parameterNames[1], DEFAULT_EXTENSION);
}
return params;
}


内部メソッドのおまけ

private String[] extensionSplit(String urlPath) {
String[] elements = urlPath.split("/");
String target = null;
if (elements[elements.length -1].contains(".")) {
target = elements[elements.length -1];
String filename = StringUtils.substringBeforeLast(elements[elements.length -1], ".");
String ext = StringUtils.substringAfterLast(elements[elements.length -1], ".");
elements = (String[]) ArrayUtils.addAll(elements, new String[]{filename, ext});
} else {
elements = (String[]) ArrayUtils.add(elements, extension);
}
return target != null ? (String[]) ArrayUtils.removeElement(elements, target) : elements;
}

2009年1月17日土曜日

jetty6@fedora rpm

jettyのrpmインストールを行う。
fedora10付属のjettyはバージョンが古いので折角だから現行最新版を導入してみる。
ソースからRPMパッケージを作成するのは挑戦してみてのですが、リポジトリを追加したりかなり面倒
だったので挫折。jettyのサイトからRPMがあったのでこちらに乗り換え。
  • RPM一式をインストール
インストールされた一式は/usr/share/jetty6から確認できる(中身はシンボリックリンク)。
ただし、jetty6-plus-6.1.14-1jpp.noarch.rpm に含まれるjetty6-plus-6.1.14.jarの中身が空っぽなので
(MANIFEST.MFくらいしか入ってない!)ので仕方なくソースをダウンロードして別途jarを作成して入れ替え。

あー、面倒くさい。

continuum導入@fedora10

continuumはservletコンテナを同梱されて配布されているが、既にjetty6.1.14を導入しているのでwar形式で導入してみる。

continuumからメール送信を利用するので/etc/jetty/start.configに以下を追記
/usr/share/java/javamail.jar always
/usr/share/java/activation.jar always
/etc/default/jetty6を作成(起動スクリプト /etc/init.d/jetty6から利用される)
JAVA_HOME=/usr/java/latest
JAVA_OPTIONS=-Dappserver.base=/usr/share/jetty6
#JETTY_USER=jetty
# JETTY_ARGSは起動時に /usr/share/jetty6/の後にくっついてしまうため使えないみたい
# java.io.FileNotFoundException: /usr/share/jetty6/-Dappserver.base (No such file or directory)
#JETTY_ARGS=-Dappserver.base

appserver.baseはcontinuumのlog4j.xmlから参照される。この定義がないとTOPディレクトリに/logs
が作成されてしまうので(root起動すると)この設定で/var/log/jetty6/continuum.logに出力されるようになる。

サーバー起動時に
Failed startup of context org.mortbay.jetty.webapp.WebAppContext@1fe4d40{/,null}

がログ(/var/log/jetty/yyyy_MM_dd.stderrout.log)に出力される。(とりあえずcontinuumは使えるので放置・・・)

これで環境が揃った。

continuum導入@fedora10

continuumはservletコンテナを同梱されて配布されているが、既にjetty6.1.14を導入しているのでwar形式で導入してみる。

continuumからメール送信を利用するので/etc/jetty/start.configに以下を追記
/usr/share/java/javamail.jar always
/usr/share/java/activation.jar always
/etc/default/jetty6を作成(起動スクリプト /etc/init.d/jetty6から利用される)
JAVA_HOME=/usr/java/latest
JAVA_OPTIONS=-Dappserver.base=/usr/share/jetty6
#JETTY_USER=jetty
# JETTY_ARGSは起動時に /usr/share/jetty6/の後にくっついてしまうため使えないみたい
# java.io.FileNotFoundException: /usr/share/jetty6/-Dappserver.base (No such file or directory)
#JETTY_ARGS=-Dappserver.base

appserver.baseはcontinuumのlog4j.xmlから参照される。この定義がないとTOPディレクトリに/logs
が作成されてしまうので(root起動すると)この設定で/var/log/jetty6/continuum.logに出力されるようになる。

サーバー起動時に
Failed startup of context org.mortbay.jetty.webapp.WebAppContext@1fe4d40{/,null}

がログ(/var/log/jetty/yyyy_MM_dd.stderrout.log)に出力される。(とりあえずcontinuumは使えるので放置・・・)

これで環境が揃った。

2009年1月5日月曜日

WebDAVのlinux client

自宅サーバーのWebDAVに会社のWindows XPからはアクセスが問題なくできるのに
Linux(fedora10)からアクセスできなくなった…

なんでダメだったか今日分かった。
httpd のアクセスログで
"OPTIONS /DAVDIR HTTP/1.1" 301 294 "-" "gvfs/1.0.3"
となっており、301が正しく解釈出来ない様子。
なんだと調べてみるとこちらのブログを見て気がついた。

httpd.confの

#
# The following directive disables redirects on non-GET requests for
# a directory that does not include the trailing slash. This fixes a
# problem with Microsoft WebFolders which does not appropriately handle
# redirects for folders with DAV methods.
# Same deal with Apple's DAV filesystem and Gnome VFS support for DAV.
#
BrowserMatch "^gvfs/1.0.[0-9]{1}" redirect-carefully

こちらの部分に該当するgvfsを追記して(バージョン部分の正規表現はある程度緩めにしてみた)
再起動したらlinux(fedora10 gvfs-1.0.3-4.fc10.i386)からもアクセス出来ました。

ホントは設定上、https限定(SSLRequireSSL)にしたかったのだけど未対応みたい(オレオレ証明書だから?)
もともとIPtableで80ポートはオープンしていないのでま、いいかと妥協。