2018年11月11日日曜日

9: 'やあ、コンソールJava UNOクライアントたち'サンプル

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

あなたのプログラムをLibreOffice/OpenOfficeインスタンス(または任意のUNOサーバー)に接続する、オフィスドキュメントを読み書きしたりインスタンスを操作したりするために。

話題


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: Javaプログラミング言語

この記事の目次


開始コンテキスト



ターゲットコンテキスト



  • 読者は、コンソールJavaプログラムからLibreOfficeまたはApache OpenOfficeのインスタンスに接続する方法を知る。
ト書き
Hypothesizer 7、Objector 9A、Objector 9Bがコンピューターの前にいる。


オリエンテーション


Hypothesizer 7
この記事では、コンソールJava UNOクライアントサンプルを取得、ビルド、実行、理解します。

Objector 9A
お前は何か誤解してるな。「LibreOfficeまたはApache OpenOfficeのインスタンスにまずコネクトしなければなりません」だと?俺は、ただCalcシートを自分のJavaプログラムで読み書きしたいだけであって、何か知らんが、LibreOfficeまたはApache OpenOfficeのインスタンスにコネクトなど断じてしなくてよい。

Hypothesizer 7
あの、...あなたは確かにそうしなければなりません。

Objector 9A
お前は耳が聞こえないのか?Calcシートを自分のJavaプログラムで読み書きしたいだけだと言ったんだ!

Hypothesizer 7
そうお聞きしましたしそう理解しました。...あの、UNOが何かをご理解されたら、私の申し上げたことをご理解されると思います。

Objector 9A
何だと?この俺に、お前のいまいましい記事を読めと言ってるのか?

Hypothesizer 7
そうは言っておりません。この記事は一定の事前知識を前提としている(ほとんどの技術ドキュメントがそうするように)と申し上げただけであり、その知識をどこであなたが得られるかは問題ではありません。

Objector 9A
まあ、お前が間違っているか俺が正しいかはどうでもいいんだ、LibreOfficeのドキュメントファイルを読み書きできさえすれば。

Hypothesizer 7
...それでは、あなたはLibreOfficeまたはApache OpenOfficeのインスタンスにまずコネクトしなければなりません。

Objector 9A
どのインスタンスだ?インスタンスなどどこにもないぞ。

Hypothesizer 7
1つ開始する必要があります、UNOサーバーとして

Objector 9B
正式に抗議します。あなたは、これが、LibreOfficeドキュメントファイルについてだと言いませんでした、Microsoft Officeドキュメントファイルについてではなく。私は、これがMicrosoft Officeドキュメントファイルについてだと思ったからここに来たのであって、私の貴重な時間が無駄になってしまいました。

Hypothesizer 7
あの、私がそう言わなかったのはそうではないからです。これは、Microsoft Officeドキュメントファイルについてでもあります...

Objector 9B
はあ?...なぜそう言わなかったわけ?

Hypothesizer 7
私がそう言ったというご抗議だと思いましたが...

Objector 9B
...じゃあ、なんでLibreOfficeについて話してるわけ?

Hypothesizer 7
マダム、過去の記事をお読みになれば、なぜかをご理解されるでしょう。

Objector 9B
そんなもの読む気はありません!

Hypothesizer 7
承知しました。


本体


1: コンソールJava UNOクライアントサンプルを取得してビルドする


Hypothesizer 7
ここに、コンソールJava UNOクライアントサンプルがあります。このシリーズに挙げられているサンプルプロジェクトをビルドする方法は過去の記事に記載されています。

Objector 9A
ZIPファイルを展開したが、3つのディレクトリがある...

Hypothesizer 7
'hiConsoleJavaUnoClients'がサンプルプログラムのプロジェクトディレクトリです。他2つのディレクトリは様々なプロジェクトから共通に使われるプロジェクトのものです。

Objector 9A
それじゃあ、俺にはサンプルプロジェクトだけが必要だ。

Hypothesizer 7
...他2つのプロジェクトも含まれているのは、それらも必要だからです。しかし、サンプルプロジェクトをビルドする際にそれらは自動的にビルドされます。

Objector 9A
それで、サンプルプロジェクトはどうすればビルドできるのか?

Hypothesizer 7
...過去の記事をお読みください。

Objector 9B
成功しない...

Hypothesizer 7
開発環境を、過去の記事(Linux用はこちらで Windows用はこちら)の記述どおりに構築されましたか?

Objector 9B
もちろんそんな記事を見たことはない。

Hypothesizer 7
私にはそれらの記事(Linux用はこちらで Windows用はこちら)を紹介することしかできません。特に、ディレクトリ名にスペースが含まれているとは想定されていないことにご注意ください。

Objector 9B
はあ?もちろん、スペースがあるとも!

Hypothesizer 7
あると想定されていません、ビルドスクリプトには。

Objector 9B
そいつは横暴だ!

Hypothesizer 7
無くて良いものをめぐって起こる面倒を省いているのです、私からすれば。

Objector 9B
いろいろ再インストールしなくちゃならんだろうが!

Hypothesizer 7
ああ、ごもっともです。ディレクトリ名やファイル名中のスペースを常に避けてきた私は、そのような状況を想像もしていませんでした。しかしながら、その要求は変わりません、少なくとも今のところは。

Objector 9B
...

ト書き
Hypothesizer 7はターミナルを開いてサンプルプロジェクトをビルドする。


2: サンプルプログラムを実行する


Hypothesizer 7
サンプルプログラムを実行する前に、LibreOfficeまたはApache OpenOfficeのインスタンスを、クライアントからのコネクションを受け付ける状態で開始しておかなければなりません(その方法は、過去の記事で知ることができます)。

Objector 9B
また「過去の記事」?

Hypothesizer 7
それは不可欠の準備であり、それを別記事にしたのは、複数の記事から参照されなければならないからです。

ト書き
Objector 9AとObjector 9Bは、言及された記事をしぶしぶ読む。

Objector 9B
...それから?

ト書き
Hypothesizer 7は、LibreOfficeのインスタンスをポート番号'2002'で開始する。

Hypothesizer 7
サンプルプログラムは、カレントディレクトリをサンプルプロジェクトディレクトリに位置させ、ファイルURLを環境に合わせて、以下のようにして実行できます。

@bash or cmd ソースコード
gradle i_executeJarTask -Pc_mainClassName="theBiasPlanet.hiConsoleJavaUnoClients.programs.HiConsoleJavaUnoClients" -Pc_commandLineArguments="socket,host=localhost,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext file://${HOME}/myData/development/hiConsoleJavaUnoClients/execution/HiConsoleJavaUnoClientsTests.odt"

ト書き
Hypothesizer 7は、カレントディレクトリをサンプルプロジェクトディレクトリに位置させて、上記コマンドをターミナルで実行する。Writer文書がオープンし、その後、すぐにクローズする。

Objector 9A
それだけか?

Hypothesizer 7
実のところ、それだけです。

Objector 9A
貧弱なサンプルだな...

Hypothesizer 7
...このサンプルは、UNOサーバーにコネクトすることに専念しております。'もっと豊かな'サンプルは将来の記事で紹介されます。

Objector 9A
どうでもいいが。

Hypothesizer 7
LibreOfficeまたはApache OpenOfficeのインスタンスがリモートホストにいる場合は、ホスト名を'localhost'から変更すればよいです、勿論、ファイアウォールが通信をブロックしなければですが。


3: サンプルプログラムを理解する



3-1: プロジェクト群構造を理解する


Hypothesizer 7
お気づきになったとおり、3つのプロジェクト、'coreUtilitiesToBeDisclosed'、'unoUtilitiesToBeDisclosed'、'hiConsoleJavaUnoClients'があります。'coreUtilitiesToBeDisclosed'には、あらゆる種類のプロジェクトで共用されるように想定されているユーティリティコードが含まれています。'unoUtilitiesToBeDisclosed'には、UNOプログラムのプロジェクト群で共用されるように想定されているユーティリティコードが含まれています。'hiConsoleJavaUnoClientsは、サンプルプロジェクトです。

Objector 9A
サンプルプログラムのソースコードを見ても、見るべきものがあまりないが...

Hypothesizer 7
コネクション生成の方法を学ぶには、'unoUtilitiesToBeDisclosed'プロジェクトの中身を見る必要があります。

Objector 9A
これらのユーティリティクラス群をそのまま使えるんじゃないのか?

Hypothesizer 7
使えます、それらのユーティリティクラス群にご満足であれば。

Objector 9A
別に満足しちゃいないが、コネクション生成にこだわりがあるわけでもない。実際、コネクションが支障なく得られさえすれば、実用上、他に何が問題になる?

Hypothesizer 7
それらのユーティリティクラス群をそのままお使いになるのであれば、その先に進むために必要なことをまず申し上げましょう。


3-2: UNOサーバーに接続した後にしなければならないこと


Hypothesizer 7
コネクションを取得した後、あなたは、UNOサーバーのUNOオブジェクト群コンテキストを必要とします。

Objector 9A
するか?

Hypothesizer 7
間違いなくされます。UNOサーバーへ接続なさったのは、UNOサーバー内に生存するUNOオブジェクトを操作されたいからです。

Objector 9A
だから何だ?

Hypothesizer 7
UNOオブジェクト群コンテキストは、UNOオブジェクトへの参照を芋ずる式に次々と取得していくための開始点です(その方法は、将来の記事で学びます)。

Objector 9A
どうでもいいが。

Hypothesizer 7
UNOオブジェクト群コンテキストは、'theBiasPlanet.unoUtilities.connectionsHandling.UnoConnection'インスタンスから、'getRemoteObjectsContext'メソッドで取得できます。

ところで、ローカルUNOプロセス(つまり、サンプルプログラムプロセス)は、自身のUNOオブジェクト群コンテキストを持っており、それは、'theBiasPlanet.unoUtilities.programsHandling.UnoProcessEnvironment'クラスインスタンスから、'getLocalObjectsContext'メソッドによって取得できます。しかし、おそらくは、それをあなたのUNOクライアントで御自分でお使いになる機会はないでしょう(それは、コネクションおよびUNOブリッジを確立するために、 ユーティリティコード内で使用されています(以下で説明します))。


3-3: UNOオブジェクト群コンテキストのラッパー


Hypothesizer 7
'unoUtilitiesToBeDisclosed'プロジェクトの'theBiasPlanet.unoUtilities.connectionsHandling.UnoObjectsContext'クラスは、UNOオブジェクト群コンテキストのラッパーです。私がこのラッパーを作成したのは、そうしなければ、UNOオブジェクト群コンテキストにプロパティを追加できないからです。

Objector 9B
なんで、私がUNOオブジェクト群コンテキストにプロパティを追加しなくちゃいけないわけ?

Hypothesizer 7
しなければならないわけでは特にありませんが、そうした追加プロパティを伝播させることが、UNOオブジェクト群コンテキストの目的です。そこで、このラッパーが必要だと考えたわけです。

Objector 9B
ふーん...

Hypothesizer 7
このラッパーは'com.sun.star.uno.XComponentContext'を実装しているので、'com.sun.star.uno.XComponentContext'が必要とされるどこにでも、このラッパーを渡すことができます。


3-4: ローカルUNOオブジェクト群コンテキストを生成する(ブートストラッピング)


Hypothesizer 7
UNOサーバーに接続する際は、1つのローカル('クライアントに生存する'という意味)UNOオブジェクトを使ってそうします。

Objector 9B
別に構わないけど、当然起きる疑問は、そのローカルUNOオブジェクトをどのように得られるかということね。

Hypothesizer 7
申し上げたとおり、UNOオブジェクトへの参照は、UNOオブジェクト群コンテキストから開始して、芋ずる式に順次得られます。

Objector 9B
当然起きる疑問は、そのUNOオブジェクト群コンテキストをどのように得られるかということね。

Hypothesizer 7
そのUNOオブジェクト群コンテキストは、もちろん、ローカルUNOオブジェクト群コンテキストであって、これは、無から生成します(これを生成することを'ブートストラッピング'と呼びます)。

Objector 9B
それはリーズナブルね。

Hypothesizer 7
実際には、ローカルUNOオブジェクト群コンテキストは、以下のようにして生成できます('theBiasPlanet.unoUtilities.programsHandling.UnoProcessEnvironment'クラスのコンストラクタ内に見られます)。

@Java ソースコード
import com.sun.star.uno.XComponentContext;
import com.sun.star.comp.helper.Bootstrap;

			XComponentContext l_originalLocalObjectsContext = Bootstrap.createInitialComponentContext (null);


3-5: UNOサーバーに接続する


Hypothesizer 7
今や、ローカルUNOオブジェクト群コンテキストを得たので、'com.sun.star.connection.XConnector'インスタンス(UNOサーバーに接続するために使用されるUNOオブジェクト)を、'com.sun.star.connection.Connector' UNOサービスのインスタンスとして得ることができます('theBiasPlanet.unoUtilities.connectionsHandling.UnoConnectionConnector'クラスの'connect'メソッド内に見られます)。

Objector 9B
'UnoServiceHandler.getServiceInstance'?

Hypothesizer 7
それは、UNOサービスインスタンスを、グローバルUNOサービス群マネージャーを介して取得するユーティリティクラスメソッドです。

Objector 9B
ふーん...

Hypothesizer 7
そして、'com.sun.star.connection.XConnector'UNOインターフェースの'connect'メソッドを、以下を接続文字列として呼ぶことにより、UNOサーバーに接続することができます:'socket,host=localhost,port=2002,tcpNoDelay=1'。

Objector 9B
ああ、接続文字列の意味は明らかね、'tcpNoDelay=1'以外は。

Hypothesizer 7
これをご参照ください('1'は'on'を意味します)。

Objector 9B
...なるほど。

Hypothesizer 7
いくつかやることが残っています。UNOブリッジを生成し、UNOサーバーのUNOオブジェクト群コンテキストを取得します。それは、以下のように行ないます('theBiasPlanet.unoUtilities.connectionsHandling.UnoConnectionsFactory'クラスの'initialize'および'setupConnection'というメソッドに見られます)。

'com.sun.star.bridge.XBridgeFactory'インスタンスを'com.sun.star.bridge.BridgeFactory'UNOサービスインスタンスとして取得する。UNOブリッジを、'com.sun.star.bridge.XBridgeFactory'UNOインターフェースの'createBridge'メソッドを、UNOブリッジ名、プロトコル('urp')、コネクション、イニシャルUNOオブジェクト群プロバイダーを指定して呼んで生成し、UNOブリッジを'com.sun.star.bridge.XBridge'インスタンスとして取得する。UNOサーバーのUNOオブジェクト群コンテキストを、'com.sun.star.bridge.XBridge' UNOインターフェースの'getInstance'メソッドを、イニシャルUNOオブジェクト名、'StarOffice.ComponentContext'を指定して呼んで取得する。

Objector 9B
「イニシャルUNOオブジェクト群プロバイダー」?「イニシャルUNOオブジェクト名」?

Hypothesizer 7
UNOブリッジを確立した後、UNOブリッジの向こうサイドからイニシャルUNOオブジェクト群を取得することができます。

Objector 9B
一体全体、イニシャルUNOオブジェクト群って何なの?

Hypothesizer 7
あるUNOオブジェクトを取得するには、それを別のUNOオブジェクトから取得する必要があり、その別UNOオブジェクトはまた別のUNOオブジェクトから取得する必要があり...、だから、開始点となる1つのイニシャルUNOオブジェクトがなければなりません。

Objector 9B
ん?単一のイニシャルUNOオブジェクトがあるの?それとも、複数のイニシャルUNOオブジェクトがあるの?

Hypothesizer 7
何個のイニシャルUNOオブジェクトを、そしてどのUNOオブジェクトを UNOブリッジ端がオファーするかは、そのUNOブリッジ端次第です。

Objector 9B
ふーん...

Hypothesizer 7
実は、私たちは、'theBiasPlanet.UnoObjectsContext'('UnoDefaultValuesConstantsGroup.c_initiallyOfferedUnoObjectName'の値)を唯一のイニシャルオファーされたUNOオブジェクトの名前とするイニシャルUNOオブジェクト群プロバイダーを指定することによって、ローカルUNOオブジェクト群コンテキストをUNOサーバーにオファーしています。

Objector 9B
それは、LibreOfficeインスタンスは'StarOffice.ComponentContext'を同様にオファーしているということなの?

Hypothesizer 7
その通りです。

Objector 9B
それじゃあ、'StarOffice.ComponentContext'は、UNOサービス名ではなく、LibreOfficeプログラム内のイニシャルUNOオブジェクト群プロバイダーで指定された名前なの?

Hypothesizer 7
その通りです。

ところで、オフィシャルのサンプル群は'StarOffice.ComponentContext'の代わりに'StarOffice.ServiceManager'を使っていますが、それが良いやり方だとは私は思いません。

Objector 9B
「StarOffice.ServiceManager」?

Hypothesizer 7
LibreOfficeまたはApache OpenOfficeのインスタンスは、その名前で、グローバルUNOサービス群マネージャーもイニシャルUNOオブジェクトとしてオファーしています。

Objector 9B
なんで良いやり方ではないわけ?

Hypothesizer 7
なぜなら、UNOの新しいモデルでは、プロパティを持ったUNOオブジェクト群コンテキストが伝播されるように意図されているからです、グローバルUNOサービス群マネージャーではなく。

Objector 9B
実害はあるの?

Hypothesizer 7
UNOサーバーがLibreOfficeまたはApache OpenOfficeのインスタンスであれば、ないと思います。デフォルトのUNOオブジェクト群コンテキストはグローバルUNOサービス群マネージャーから取得でき、LibreOfficeおよびApache OpenOfficeはデフォルトUNOオブジェクト群コンテキストをどのみち提供するようです。しかし、理論的には、UNOサーバーは、私たちが誰か(例えば、私たちが指定するイニシャルUNOオブジェクト名に反映される)によって異なる、異なるプロパティを持ったUNOオブジェクト群コンテキストを私たちに提供する可能性があります。


3-6: コネクションを切断する


Hypothesizer 7
コネクションは以下の手順で切断できます('theBiasPlanet.unoUtilities.connectionsHandling.UnoConnection'クラスの'disconnect'メソッドに見られます)。

UNOブリッジUNOオブジェクトの 'com.sun.star.lang.XComponent'UNOインターフェースの'dispose'メソッドを呼び出します。


4: オフィスインスタンスに接続する、より単純な方法があるが...


Hypothesizer 7
実は、オフィスインスタンスに接続する、より単純な方法があります、しかし、その方法にはいくつか制限があり、それらは、ある種のケースにおいて致命的です。

第1に、その方法では、UNOクライアントは、UNOクライアントのコンピューターでUNOクライアントのオペレーティングシステムユーザーによって起動されたオフィスインスタンスだけに接続できます。

それは深刻です、なぜなら、UNOクライアントが真剣なサーバープログラムであるとき、オフィスインスタンスにのみ必要とされる権限を、オフィスインスタンスに限定することができず、UNOクライアントにも与えなければならなくなり、また、オフィスインスタンスの作業負荷を別のコンピューターへ分離することができないからです。

Objector 9A
オフィスインスタンスがいるリモートコンピューターに中継Webアプリケーションか何かをセットアップして、俺の元のプログラムにはその中継点を介してオフィスインスタンスにアクセスさせればいいだけだ。

Hypothesizer 7
確かに、そうした代替手段はあります、しかし、それは、大抵、あなたのプログラムをオフィスインスタンスに直接、接続するよりもよりも多くの作業とより大きなオーバーヘッドを引き起こすことになるでしょう。

Objector 9A
そうかい?

Hypothesizer 7
そう思います、なぜなら、接続確立のためだけにより多くの行を書くだけというのに比較して、あなたの方法では、オフィスインスタンスへのアクセス毎に、プロキシインターフェイスを自ら実装する必要があります。

Objector 9A
ふーむ...

Hypothesizer 7
第2に、より単純な方法では、UNOクライアントは、接続切断イベントを受け取れません。

Objector 9A
それが問題かな?

Hypothesizer 7
ある種のケースにおいては望ましくないかもしれません。例えば、UNOクライアントがユーザーのインプットを待つものである場合、ユーザーは、コネクションが断ち切られた時に即座に通知を受ける方が嬉しいでしょう、コネクションが既に切断されているがゆえにどうせ無駄になる運命にある多くのデータを入力するまま放置されるよりも。

Objector 9A
コネクションを再確立して、ユーザーにシームレスに操作継続させるわけにはいかないのか?

Hypothesizer 7
それは、不可能ではありませんが、多くの場合には困難でしょう、なぜなら、コネクションを再確立するだけでは十分でないからです。オフィスインスタンスとUNOクライアントの状態を再構築する必要があります。

Objector 9A
「状態」とは何のことだ?

Hypothesizer 7
例えば、オフィスインスタンスは、異常終了する前、あるドキュメントを開いて、カーソルを特定の箇所に位置づけており、UNOクライアントは、いくつかのUNOプロキシを持っていたかもしれません。

Objector 9A
ああ...

Hypothesizer 7
第3に、より簡単な方法は、UNOサーバーの作成へと発展させることができません。

Objector 9A
そんなの作成したがるべきかね?

Hypothesizer 7
したがるべきとは申しませんが、もしも、したければ...

Objector 9A
なぜ、したがるのことがありえるのか俺には理解できん。

Hypothesizer 7
例えば、一部の作業をUNO拡張機能からあるUNOサーバーへ委譲したいと思うかもしれません。

Objector 9A
その作業は、HTTPか何かを介して委譲できないのかね?

Hypothesizer 7
多くの場合それでいいかもしれませんが、その方法では、サーバーに必要なデータにアクセスさせるのに苦労しなければならないかもしれません。

Objector 9A
どういう意味だ?

Hypothesizer 7
UNO拡張機能は多くの場合、UNO関連データを操作しているでしょうが、サーバーがそうしたデータの一部にアクセスしたい場合、サーバーがそうしたデータをあるがままに操作できるようにするほうが、そうしたデータの各々をHTTPを介して渡せるフォーマットへ変換して、変換されたデータをやり取りするよりも、より便利かもしれません。

Objector 9A
ふーむ...


5: 結びとその先


Hypothesizer 7
これで、コンソールJava UNOクライアントから、任意のUNOサーバーに接続することができるようになりました。

当サンプルを'コンソール'クライアントと特に断わったのは、GUI Java UNOクライアントサンプルを次の記事で紹介するからです。

GUI Java UNOクライアントは、UNO上、コンソールJava UNOクライアントと違う必要がないように思われるかもしれませんが、1つ違いがあります。UNOクライアントが長い間動作し続けると想定されるため、UNOサーバーまたはネットワークによって引き起こされるコネクション切断を検知することがより適切となります。

また、他のプログラミング言語(C++およびC#)によるUNOクライアントも見ていきます、将来の記事にて。


参考資料


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