(solved) com.sun.star.comp.helper.BootstrapException: no office executable found!

Add C:\Program Files (x86)\LibreOffice 4\program\classes to classpath.


JavaLibreOffice を操作しようとして最初で躓きました。。。が、どうにか解決しました。


前提知識的なところとして、大雑把には次のような手続きで処理します。

  1. LibreOffice をサーバとして起動
  2. LibreOffice に接続してリモート(サーバ側の)コンテキスト(オブジェクト)を取得
  3. リモートコンテキストを使って目的の操作を行う

LibreOffice を起動して接続して…という処理を自前で書いてもいいのですが、Bootstrap.bootstrap を使うと LibreOffice の起動からリモートコンテキストの取得までをよしなにやってくれます。

やってくれるはずなのですが上記のエラーが出ます。


"no office executable found!" の発生箇所。
soffice.exe(Windows の場合)のフルパスを取得しようとして失敗しています。

File fOffice = NativeLibraryLoader.getResource(com/sun/star/comp/helper/Bootstrap.getClassLoader(), sOffice);

if(fOffice == null)
    throw new BootstrapException("no office executable found!");

さらに NativeLibraryLoader.getResource を見てみると次のようなことをやっていて、要するに、使っているライブラリのパスをクラスローダから取得し、そのライブラリのあるディレクトリ、またはその親ディレクトリに soffice.exe があるかを調べています。

java.net.URL urls[] = ((URLClassLoader)loader).getURLs();
for(int i = 0; i < urls.length; i++) {
    // file:///C:/path/to/file.ext のようなURLをパスに変換
    File path = UrlToFileMapper.mapUrlToFile(urls[i]);
    if(path == null) continue;

    // path の親ディレクトリ
    File dir = path.isDirectory() ? path : path.getParentFile();
    if(dir == null) continue;

    path = new File(dir, name);
    if(path.exists()) // soffice.exe が存在していればOK
        return path;

    // path の親ディレクトリの親ディレクトリ
    dir = dir.getParentFile();
    if(dir == null) continue;

    path = new File(dir, name);
    if(path.exists()) // soffice.exe が存在していればOK
        return path;
}

C:\Program Files (x86)\LibreOffice 4\program\classes\ などの場所に置いてあるライブラリを使うことを前提と作られているため、この jar を別のところに置き、それをクラスパスに追加して使おうとすると soffice.exe が見つけられず上記のエラーが発生したということのようです。



なので、対処としては

  1. C:\Program Files (x86)\LibreOffice 4\program\classes\unoil.jar などをクラスパスに追加する(ファイルでなくディレクトリの追加でも可)か、または
  2. soffice.exe の場所を明示的に指定する(Bootstrap.bootstrap を使わず起動〜接続処理を自前で書く)。
  3. または、自前で書くのがめんどくさい場合は Java solution "no office executable found!" (View topic) • Apache OpenOffice Community Forum に置いてある bootstrapconnector.jar を使う

あたりになると思います。

検証用テストコード

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;

import org.junit.Test;

import com.sun.star.comp.helper.Bootstrap; // juh.jar
import com.sun.star.uno.XComponentContext; // ridl.jar

public class BootstrapTest {

    @Test
    public void test() throws Exception{

        ClassLoader loader = com.sun.star.comp.helper.Bootstrap.class.getClassLoader();
        java.net.URL urls[] = ((java.net.URLClassLoader)loader).getURLs();
        for(java.net.URL url : urls){
            System.err.println(url);
        }

        XComponentContext remoteContext = Bootstrap.bootstrap();

        assertThat(remoteContext, is(notNullValue()));
    }

}

環境