Java用WebDAVクライントライブラリsardine

WindowsXPコマンドラインから使えるWebDAVクライアントを探していたのですが、
あまりニーズにあったものが見つけられませんでした。


まず、cadaverを試しましたが、Cygwin版と本家とで微妙な差異があって
トラブルシューティングできずに諦めました。
他にもWindowsXPで動作するものを探しましたが、
GUIでしか操作できないものがほとんどでした。


Javaのようなプラットホーム非依存な言語のライブラリなら
Windowsでもうまくいくのではないかと思って、Javaのライブラリを探してみました。


メンテナンスされていないものが多い中、sardine
使いやすいようにみえました。


これはJava用ライブラリですが、
ちょっとしたファイルのコピーがしたいだけなので、
Groovyで使うことにしました。
こういうスクリプト的なことをやるにはJavaはめんどうなので。

WebDav.groovy

sardineの機能を使いやすいようにまとめたクラスです。
Groovyの@Grabを使うことで、pom.xmlのようなものが無くても、
スクリプトファイルだけで依存関係を解決できます。
(ただし、初回実行時には依存ライブラリをダウンロードするのでとても遅くなります)

@Grab(group = 'com.googlecode.sardine', module = 'sardine', version = '314')
@GrabResolver(name = 'sardine-google-svn-repo', root = 'http://sardine.googlecode.com/svn/maven/')
import com.googlecode.sardine.Sardine
import com.googlecode.sardine.SardineFactory
import org.apache.http.client.ClientProtocolException
import com.googlecode.sardine.DavResource
import com.googlecode.sardine.impl.SardineException

class WebDav {

    final File localRootDir

    final ConfigObject conf

    // 依存関係に含めてくれていないのでしょうがなく追加
    @Grapes([
    @Grab(group = 'org.slf4j', module = 'slf4j-simple', version = '1.6.4'),
    @Grab(group = 'org.slf4j', module = 'slf4j-api', version = '1.6.4')
    ])
    final Sardine sardine

    /**
     * コンストラクタ
     * @param configScript 設定スクリプト
     */
    WebDav(String configScript) {
        // 設定ファイル
        File configFile = new File(configScript)
        assert configFile.exists() : configFile.absolutePath
        conf = new ConfigSlurper().parse(configFile.toURL())
        println "conf = $conf"

        // ローカルディレクトリ
        localRootDir = new File(conf.local.dir as String)
        assert localRootDir.exists() : localRootDir.absolutePath

        // Sardineインスタンス
        sardine = SardineFactory.begin(
                conf.webdav.user as String,
                conf.webdav.pass as String
        )
    }

    boolean exists(String davResource) {
        try {
            getResource(davResource)
            return true
        } catch (SardineException e) {
            if (e.statusCode == 404) return false
            throw e
        }
    }

    boolean isDirectory(String davResource) {
        return getResource(davResource).directory
    }

    private DavResource getResource(String resource) {
        def list = sardine.list(toRemoteUrl(resource), 0)
        assert list.size() == 1
        return list[0]
    }

    List<String> list() {
        return list("")
    }
    List<String> list(String dir){
        return sardine.list(toRemoteUrl(dir)).collect {
            it.path
        }
    }

    void put(String resource) {
        def file = toLocalFile(resource)
        if (!file.exists()) {
            throw new FileNotFoundException(file.getAbsolutePath())
        }
        file.withInputStream { input ->
            String to = toRemoteUrl(resource)
            println "put file [${file.absolutePath}] to [${to}]"
            URI uri = URI.create(to)
            sardine.enablePreemptiveAuthentication(uri.host)
            sardine.put(to, input, "text/plain")
        }
    }

    void get(String resource) {
        def file = toLocalFile(resource)
        if (!file.exists()) {
            assert file.createNewFile() : file.absolutePath
        }
        file.withOutputStream { output ->
            String from = toRemoteUrl(resource)
            println "get resource [${from}] to [${file.absolutePath}]"
            sardine.get(from).eachByte { b ->
                output.write(b as int)
            }
        }
    }

    private String toRemoteUrl(String resource) {
        return "${conf.webdav.url}/${resource}"
    }

    private File toLocalFile(String pathFromRootDir) {
        return new File(localRootDir, pathFromRootDir);
    }
}

ストリームのようなリソースの扱いにクロージャが使えるので、
シンプルに記述できます。try-finallyがひとつもありませんがちゃんと解放処理はされています。

slf4jというライブラリを使っているのに、依存関係に含めてくれていないので
しょうがなくこちらで足している箇所があります。sardinのソースリポジトリを見ると、
どうもEclipseメインで開発しているぽいので、きっと依存関係に入れ忘れたんでしょう。

config.groovy(設定ファイル)

環境依存値をまとめて書いています。
ConfigSlurperを使うことでJSONのような記述が可能になります。

webdav {
    url = "https://url/of/webdav/server/"
    user = "scott"
    pass = "tiger"
}

local {
    dir = "temp"
}

実行スクリプト(Hoge.groovy)

こんなかんじで呼び出します。

def dav = new WebDav("config.groovy")
dav.get("index.org")
//dav.put("index.org")

実行

> ls
Hoge.groovy     WebDav.groovy   config.groovy   temp/

> groovy Hoge.groovy 

これで、WebDAV上のindex.orgファイルがtempフォルダにコピーされます。