ラベル 外部JavaプログラムでUNOを使用する(LibreOfficeまたはApache OpenOfficeのドキュメントを操作する)方法 の投稿を表示しています。 すべての投稿を表示
ラベル 外部JavaプログラムでUNOを使用する(LibreOfficeまたはApache OpenOfficeのドキュメントを操作する)方法 の投稿を表示しています。 すべての投稿を表示

2017年10月22日日曜日

1: シリーズ「外部JavaプログラムでUNOを使用する(LibreOfficeまたはApache OpenOfficeのドキュメントを操作する)方法」の前書き

<このシリーズの、前の記事 | このシリーズの目次 |

本文 START

本シリーズは、メインシリーズ、「UNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を開発する」の記述を外部JavaプログラムでのUNOの使用に適用するための、メインシリーズへの補完である

話題: UNO (Universal Network Objects)

話題: LibreOffice

話題: Apache OpenOffice

話題: Javaプログラミング言語

本シリーズの目的

-Hypothesizer

既に、シリーズ、「UNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を開発する」があり、これはJavaによるUNOコンポーネントからなるUNO拡張機能を開発することについてのものだが、その記述のほとんどは、外部JavaプログラムでUNOを使用することにもあてはまる。というのも、それらは、UNOを使用することについてであって、UNOは、UNO拡張機能で使おうが外部プログラムで使おうが同じだから。

-Rebutter

「外部プログラム」というのはどういう意味なのか?

-Hypothesizer

「外部プログラム」で私が意味しているのは、LibreOfficeプログラムの外で動くプログラムのことで、典型的には、'main'メソッドで開始する普通のプログラムだ(私はJavaプログラムについてのみ話している)。

-Rebutter

それは、「外部プログラム」の正確な説明ではないのじゃないか?

-Hypothesizer

うーん、 . . . まあそうだ。. . . ふーむ、「外部プログラムとはスタンドアロンプログラムのことだ。」のような単純な説明が好まれ、「わかりやすい説明」だとみなされる。それ以上の正確な説明は、嫌われる公算が高く、即座に投げ出される公算はさらに高い、ほとんどの人からは。

-Rebutter

私は構わない。

-Hypothesizer

まあ、君に話しているんで、ほとんどの人にではないから、君が構わないなら確かにそれでいい。. . .

厳密に言えば、外部プログラムというのは、望むコンポーネントコンテキスト(「コンポーネントコンテキスト」は、UNO環境へのハンドル)がただ与えられるのではない任意の環境だ。 

-Rebutter

ははあ . . .

-Hypothesizer

理解できるか?

-Rebutter

実のところ、できる。

-Hypothesizer

それはよかった。補足説明をすると、操作したいUNO環境のコンポーネントコンテキストをひとたび得れば、そのUNO環境に対して許されていることは何でもできる。したがって、そのコンポーネントコンテキストをどう得るかが問題だ。

-Rebutter

UNO拡張機能の場合、UNO拡張機能が登録された先のUNO環境のコンポーネントコンテキストがUNO拡張機能に渡される。しかし、「普通の」Javaプログラムの場合、コンポーネントコンテキストが奇跡的に、おそらくは天国からそこに渡されたりはしない。

-Hypothesizer

だから、我々は、望むコンポーネントコンテキストを自ら取りに行かなければならない。言い換えると、あるプログラムがその状態にあれば、そのプログラムは外部プログラムだ、それが「普通の」プログラムであろうが、ウェブアプリケーションであろうが、アプレットであろうが、何であろうが、UNO拡張機能であってもだ。

-Rebutter

UNO拡張機能が、そのUNO拡張機能が登録された先のUNO環境とは別のUNO環境にアクセスしたければ、それは、望むUNO環境に対して外部プログラムだ。

-Hypothesizer

本当のところを言うと、ただ「外部」と言うのはナンセンスだ。「何に対して外部なのか」と私なら言うだろう。

-Rebutter

明らかに、我々が言っているのは、望むUNO環境に対して「外部」ということだ。

-Hypothesizer

繰り返すが、コンポーネントコンテキストが得られた後についてのメインシリーズのすべての記述は、どんな「外部プログラム」にも完全にあてはまる。例えば、スプレッドシートセルから読んだりスプレッドシートセルに書いたりすることについての記述は、外部UNOプログラムに完全にあてはまる。適切なコンポーネントコンテキストを使えばよいだけだ。

同じ記述を繰り返したくないので、このシリーズでは、外部UNOプログラムのみについての事柄だけを記述する。

-Rebutter

オーケー。

外部UNOプログラムを開発するための環境を構築する方法

-Hypothesizer

まず、外部UNOプログラムを開発するための環境を構築しなければならない。外部UNOプログラムを開発するための環境はUNO拡張機能を開発するためのものとほとんど同じなので、メインシリーズの対応する記事(Linux用はここ、Windows用はここ)を参照してもらおう。

-Rebutter

外部UNOプログラムを開発するための環境とUNO拡張機能を開発するための環境の違いは何なのか?

-Hypothesizer

外部UNOプログラムを開発するための環境は、UNO拡張機能を開発するための環境のサブセットだ。外部UNOプログラムを開発するには不必要なものがいくつかある。

Linux用において、wmctrlは不要だ。LibreOfficeプログラムインスタンスを落とす必要はないから。そして、Linux、Windows両方において、UNOクライアント(サーバーではなく)だけを開発するのであれば、LibreOffice SDKは必ずしも必要ない。文書を含んでいるから役には立つが。

-Rebutter

それがUNO拡張機能の開発に必要だったのは、UNOIDLファイル(UNOIDLファイルが何かを知るには、ここを参照)をコンパイルする'idlc'のようなコマンドを含んでいるからだ。

-Hypothesizer

そう。UNOクライアントだけを作るつもりであれば、UNOコンポーネント(UNOコンポーネントが何かを知るには、ここを参照)は作る必要はないだろう(作ることはできるが)。

-Rebutter

それでは、我々が使うJarファイルはLibreOffice自体に含まれているのか、SDKにではなく?

-Hypothesizer

そうだ。

もし、Maven、make、Eclipse、NetBeans、その他なんでも、他のビルディングツールを使いたければ、もちろん、使うことができる。外部UNOクライアントを開発する場合、ただ、クラスパスに必要なJarファイルを含めるというだけの問題だ。

-Rebutter

我々は、我々のGradleビルドスクリプトやAntビルドファイルに、UNOIDLファイルをコンパイルし、UNOデータタイプ「マージされた」レジストリファイルを作り、UNO拡張機能ファイルを作り、UNO拡張機能をLibreOfficeに登録し、LibreOfficeプログラムインスタンスを再起動するという機能を実装したが、その機能は、外部UNOクライアントを開発するためには必要でない。

ローカルホストのLibreOfficeプログラムインスタンスのUNO環境のコンポーネントコンテキストを取得するシンプルな方法があるが、 . . .

-Hypothesizer

ローカルホストのLibreOfficeプログラムインスタンスのUNO環境のコンポーネントコンテキストを取得したいだけである場合には、以下のようなシンプルな方法がある(これはJNIを使うので、実行時にjava.library.pathを適切にセットしなければならないことには気をつけよう。さもなければ、例外、"java.lang.UnsatisfiedLinkError: no jpipe in java.library.path"が起きる)。

@Java Source Code
import com.sun.star.uno.XComponentContext;
import com.sun.star.comp.helper.Bootstrap;

  XComponentContext l_remoteComponentContextInXComponentContext = Bootstrap.bootstrap ();
-Rebutter

それだけ?

-Hypothesizer

それだけだ。これで、コンポーネントコンテキストが得られたので、シリーズ、「UNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を開発する」の記述のほとんどを、我々の外部UNOプログラムに、このコンポーネントコンテキストを使用するだけで、適用できる。

-Rebutter

しかし、接続先のLibreOfficeプログラムインスタンスは、ローカルホスト上にいなければならない(我々の外部UNOプログラムと同じコンピューター内にいなければならない)ということか?

-Hypothesizer

そのとおり。実際には、このメソッドは、LibreOfficeプログラムインスタンスに接続するのに名前付きパイプを使用している。

-Rebutter

名前はどのように決められるのか?

-Hypothesizer

メソッドは、呼ばれる度にランダムな名前を生成し、その名前をLibreOfficeプログラムインスタンス(LibreOfficeプログラムインスタンスが起動していなければ、メソッドがインスタンスを起動する)に登録する。

-Rebutter

それでは、LibreOfficeプログラムインスタンスが動作し続けている間に我々の外部UNOプログラムを多数回実行したら、LibreOfficeプログラムインスタンスに多数の名前が蓄積されるのか?

-Hypothesizer

そうなると思われる。確かに、名前付きパイプの数に制限はあるが、 . . .

-Rebutter

まあ、実際上は、ほとんどの場合、何の害もないかもしれない、ただ、無駄なことをしているという気はする。

-Hypothesizer

ともかく、この方法でハッピーであれば、このシリーズをこれ以上読む必要はない。知るべき残りのことは、メインシリーズ、「UNO拡張機能(LibreOffice拡張機能またはApache OpenOffice拡張機能)を開発する」に書かれている(または書かれるであろう)。

これが、LibreOfficeプログラムインスタンス(ローカルホストにあろうがリモートホストにあろうが)またはその他の任意のUNO環境のコンポーネントコンテキストを取得するもっとフレキシブルな方法だ

-Hypothesizer

リモートホストのLibreOfficeプログラムインスタンスに接続したいとか、接続の切断を検出したいとか、外部UNOプログラムからUNOオブジェクトを公開したいとかいうのであれば、別の方法を用いなければならない。

この方法は1行では書けないので、ユーティリティクラス(ここからダウンロードできる)を作った。

長々しい講釈は未来の記事に譲って、あるLibreOfficeプログラムインスタンスのコンポーネントコンテキストを以下のように取得できる('a_serverUrl'には'socket,host=localhost,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext'を渡す)。

@Java Source Code
package test.unobatchclienttest1;

import java.time.LocalDateTime;
import java.math.BigDecimal;
import com.sun.star.uno.XComponentContext;
import thebiasplanet.unoutilities.programshandling.UnoEnvironment;
import thebiasplanet.unoutilities.connectionshandling.UnoConnectionConnector;
import thebiasplanet.unoutilities.connectionshandling.UnoConnection;

final public class Test1Test {
 public static void test (String a_serverUrl, String a_spreadSheetsDocumentFileUrl) {
  UnoEnvironment l_unoEnvironment = null;
  UnoConnection l_unoConnection = null;
  try {
   l_unoEnvironment = new UnoEnvironment (null, LocalDateTime.now ().toString (), null);
   UnoConnectionConnector l_unoConnectionConnector = new UnoConnectionConnector (l_unoEnvironment.getLocalComponentContext ());
   l_unoConnection = l_unoConnectionConnector.connect (a_serverUrl, null);
   XComponentContext l_remoteComponentContextInXComponentContext = l_unoConnection.getRemoteComponentContextInXComponentContext ();
  }
  catch (Exception l_exception) {
   System.out.println (l_exception.toString ());
  }
  finally {
   if (l_unoConnection != null) {
    l_unoConnection.disconnect ();
   }
  }
 }
}
-Rebutter

それでは、我々は、名前付きパイプではなくソケットを使うわけだ。リモートホストのLibreOfficeプログラムインスタンスに接続したい場合は、'localhost'という部分を変えればよいだけだ。

-Hypothesizer

LibreOfficeプログラムインスタンスは以下のように起動しておかなければならない。

Linuxの場合:

@bash Source Code
soffice --accept=socket,host=localhost,port=2002\;urp\;

Windowsの場合:

@cmd Source Code
soffice --accept=socket,host=localhost,port=2002;urp;

または、LibreOfficeプログラムインスタンスをヘッドレス(おおむね、不可視という意味)にしたければ、'--headless'というパラメータを追加してもよい。

-Rebutter

それは、LibreOfficeプログラムインスタンスをポート、'2002'を開けて起動する。. . . そのポートが開いていない状態ですでにLibreOfficeプログラムインスタンスが既に上がっていたらどうすればよい?

-Hypothesizer

まったく同じコマンドが、インスタンスが上がったままでそのポートを開く。

-Rebutter

分かった。

例えば、スプレッドシートのセルに読み書きしてみよう

-Hypothesizer

実は、'thebiasplanet.unoutilities.jar'(上記でダウンロードしたプロジェクトの1つからビルドされるJarファイル)はスプレッドシートにアクセスするユーティリティクラス群を含んでいるので、スプレッドシートのセルに以下のように(以下のインポート命令とその下のコードをそれぞれ上記コードの適切な場所に挿入する)読み書きできる。'a_spreadSheetsDocumentFileUrl'にはスプレッドシートドキュメントファイル(ファイルには'Sheet1'という名前のシートがなければならない)のURLを渡す。

@Java Source Code
import thebiasplanet.unoutilities.spreadsheetshandling.UnoSpreadSheetsDocument;
import thebiasplanet.unoutilities.spreadsheetshandling.UnoSpreadSheet;

   UnoSpreadSheetsDocument l_spreadSheetsDocument = UnoSpreadSheetsDocument.openSpreadSheetsDocumentFile (l_remoteComponentContextInXComponentContext, a_spreadSheetsDocumentFileUrl, true);
   try {
    UnoSpreadSheet l_spreadSheet = l_spreadSheetsDocument.getSpreadSheet ("Sheet1");
    Object l_cellValue = l_spreadSheet.getSpreadSheetCell (0, 0).getValue ();
    System.out.println (String.format ("The cell value at the 0, 0 cell is %s.", l_cellValue.toString ()));
    l_spreadSheet.getSpreadSheetCell (1, 0).setValue (new BigDecimal ("12.340"));
    l_spreadSheetsDocument.store ();
   }
   catch (Exception l_exception) {
    System.out.println (l_exception.toString ());
   }
   finally {
    l_spreadSheetsDocument.close ();
   }
-Rebutter

これらのユーティリティクラス群は、メインシリーズのいくつかの記事(特に、これとそれに続くいくつかの記事)で記述されていることをまとめたものだ。

-Hypothesizer

そうだ。

実は、上のテストプログラムは上のzipファイルに含まれており、ユーティリティJar群(実際には、2つ、'thebiasplanet.coreutilities.jar' and 'thebiasplanet.unoutilities.jar'がある)とテストプログラムを以下のように、ビルドし、テストプログラムを実行できる。

1. zipファイルを、ディレクトリ構造を維持して展開する。

2. 'commonBuild01.gradle'または'commonBuild.properties'のいくつかのパラメータ('LIBREOFFICE_DIRECTORY_NAME'および'LIBREOFFICE_SDK_DIRECTORY_NAME')を変更しなければならないかもしれない(ここを参照)。

3. ターミナルで、カレントディレクトリを'coreUtilitiesToDisclose'に変更し、'gradle'または'ant'を実行する。

4. カレントディレクトリを'unoUtilitiesToDisclose'に変更し、'gradle'または'ant'を実行する。

5. 'unoUtilitiesTestToDisclose'の中で、'Test.java'のメインメソッドの内容が以下のようになっていることを確認する。

@Java Source Code
  test.unobatchclienttest1.Test1Test.test (a_arguments [0], a_arguments [1]);

6. カレントディレクトリを'unoUtilitiesTestToDisclose'に変更し、'gradle'または'ant'を実行する。

7. Linuxのbash上のGradle用には、以下を実行する(他の環境では'$(pwd)'部分を変更する。特にWindowsでは'%CD%'にする)。

@bash Source Code
gradle test -PCOMMAND_LINE_ARGUMENTS="socket,host=localhost,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext file://$(pwd)/execution/TestSpreadSheetsDocument1.ods"

Linuxのbash上のAnt用には、以下を実行する(他の環境では'$(pwd)'部分を変更する。特にWindowsでは'%CD%'にする)。

@bash Source Code
ant test -DCOMMAND_LINE_ARGUMENTS="socket,host=localhost,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext file://$(pwd)/execution/TestSpreadSheetsDocument1.ods"

後者の方法のメリット

-Hypothesizer

後者の方法のメリットを簡単に述べたが、それらをもっと詳しく説明しよう。

-Rebutter

オーケー。

-Hypothesizer

リモートホスト上のLibreOfficeプログラムインスタンスに接続できる(そのインスタンスがポートを開いていれば)。

-Rebutter

いいだろう。

-Hypothesizer

接続の切断イベントのリスナーを登録できる。

上のサンプルのようなバッチ型クライアントプログラムにそのようなリスナーを登録する必要性を感じないかもしれないが、不定期の間生き続けるGUIクライアントを作る場合、そうしたイベントを検出するのは便利であり得る。

-Rebutter

すると、接続が切断された時、それはおそらくLibreOfficeプログラムインスタンスが正常にせよ異常にせよ終了した場合やネットワークが断ち切られた場合だろうが、クライアントはそのイベントを即座に検出するわけだ。

-Hypothesizer

そうだ。

-Rebutter

まあ、それはかっこ良く聞こえるが、その機能がどのように必要なのかよく見えないのだが。

-Hypothesizer

うーむ、リモートUNO環境にアクセスを試みた際に切断を検出できればそれで十分だと意味かな?

-Rebutter

そう、少なくとも、ほとんどの場合では。

-Hypothesizer

そうかもしれない。. . . 切断によって引き起こされるエラーを適切に処理すればよいだけだ。実際、その方法で特に不便を感じたことはない、例えば、JDBC接続で。それに必要であれば、ポーリングしてもよい。

-Rebutter

. . .

-Hypothesizer

別のメリットとして、UNOオブジェクトを公開するUNOサーバーを作ることができる。

-Rebutter

ははあ。すると、例えば、何らかの集中的な計算やデータアクセスを行なうUNOサーバーを作ることができ、LibreOfficeがそうした集中的な作業をそのサーバーに、おそらくUNO拡張機能やBasicマクロを通じて、移譲できるようにすることができる。

-Hypothesizer

そう。そうすれば、集中的な作業を行なうのに適しているとは思われないBasicマクロでそのような作業を行なう必要がない。

外部UNOプログラムを開発するのが唯一の関心事である場合にメインシリーズで見るべきところ

-Rebutter

メインシリーズの内の何が外部UNOプログラムに適用でき、何ができないのかのもっと具体的なガイダンスがないか?

-Hypothesizer

外部UNOクライアントだけを開発したいのであれば、2つのサンプルUNO拡張機能についての記事のほとんどは関係ないだろう。他方、外部UNOサーバーも開発したいのであれば、それらの記事のほとんども関係ある。というのも、おそらくは、UNOコンポーネントを作り、それらをUNOサービスとして登録するだろうから。

-Rebutter

他の記事はどうか?

-Hypothesizer

それらは、一般的にUNOを使用することについてのものだ。だから、それらは、外部UNOプログラムについても同じだ。

特に、UNOを思い通りに使用するためには、UNOの基本概念の正しい理解が必要だろう。その理解がなければ、UNOのAPI文書が言っていることを十分には理解できないだろう。

本文 END

<このシリーズの、前の記事 | このシリーズの目次 |

0: シリーズ「外部JavaプログラムでUNOを使用する(LibreOfficeまたはApache OpenOfficeのドキュメントを操作する)方法」の目次

| このシリーズの目次 | このシリーズの、次の記事>

| このシリーズの目次 | このシリーズの、次の記事>