2021年9月5日日曜日

62: UNOサービスをJavaで作成する

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

当該UNOコンポーネントをリモートプログラミング言語環境、例えばPythonマクロ、からインスタンス化するために。

話題


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

この記事の目次


開始コンテキスト



ターゲットコンテキスト



  • 読者は、自身のグローバルUNOサービスをJavaで作成する方法を知る。

オリエンテーション


UNOインターフェイスを作成・登録し、当該マッピングイメージ群を生成する方法についての記事があります。

Pythonマクロを、ユーザー所有またはアプリケーション所有ドキュメント内拡張機能内マクロとして作成することについての記事があります。


本体

ト書き
Hypothesizer 7、Objector 62A、Objector 62Bが、コンピューターの前にいる。


1: なぜ、UNOサービスが望まれるのか


Hypothesizer 7
UNOサービスを作成することの目的は、当該UNOコンポーネントをリモートプログラミング言語環境からインスタンス化することです。

Objector 62B
「当該UNO コンポーネント」?どのUNOコンポーネントよ?

Hypothesizer 7
マダム、UNOサービスというものは、荒く言って、あるUNOサービスマネージャーに登録されたUNOコンポーネントのことなので、UNOサービスを作成しようという意図を持たれた時点で、あなたは既にそのUNOコンポーネントを思い描いておられるはずです。

Objector 62B
はずですって?「たまには、UNOサービスを持ってみてもいいかも」とただ思っただけなんだけど。

Hypothesizer 7
. . . あなたは、1つUNOコンポーネントを選択しなければなりません。

Objector 62B
代わりにあなた選んでくださる?

Hypothesizer 7
いいえ、できません、マダム。

Objector 62B
不親切ねえ。

Hypothesizer 7
親切さの問題ではありません。意味がありません。

Objector 62A
「リモートプログラミング言語環境」とはどういう意味だ?

Hypothesizer 7
サー、リモートプログラミング言語環境とは、当該UNOコンポーネントがインスタンス化されるプログラミング言語環境ではないプログラミング言語環境のことです。

Objector 62A
それは、禅の公案か?

Hypothesizer 7
大抵は、当該UNOコンポーネントは、LibreOfficeまたはApache OpenOffice JVM内にインスタンス化され、例えば、Pythonマクロは、リモートプログラミング言語環境です。

Objector 62A
単に'別のプログラミング言語から'と言えばよかったのだ。

Hypothesizer 7
いいえ、よくありませんでした: リモートプログラミング言語環境は、Java UNOクライアントかもしれません。

Objector 62A
. . . なぜ、それは「リモート」でなければならないんだ?

Hypothesizer 7
別にリモートで「なければならない」ことはありませんが、もしもローカルであるのであれば、特にUNOサービスを必要とはされないでしょう、なぜなら、当該UNOコンポーネントはただの'new'オペレーターでインスタンス化できるのですから。

Objector 62A
それじゃあ、UNO コンポーネントがリモートであるときは、常にUNOサービスを作らないといけないのか?

Hypothesizer 7
常にではありません: もしも、当該UNOコンポーネントは別の普通のUNOオブジェクトのあるメソッドによってインスタンス化されるというのであれば、UNOサービスは必要でないでしょう。

Objector 62A
「普通のUNOオブジェクト」とはどういう意味だ?ていうか、'異常な'UNOオブジェクトって何だ?

Hypothesizer 7
UNOサービス群マネージャーではないUNOオブジェクトのことです、UNOサービス群マネージャーが特に「異常」というわけではありませんが。

Objector 62A
それじゃあ、私のUNOコンポーネントたちはみな、普通のUNOオブジェクトたちからインスタンス化されるというようにすれば、私にはUNOサービスなど一切必要ないということか?

Hypothesizer 7
しかし、それら普通のUNOオブジェクトたちはどこからやってきたのでしょう?通常、あなたのUNOオブジェクトたちの内の最初のものは、あるUNOサービス群マネージャーから生成され、その後に、その最初のUNOオブジェクトがあなたのUNOコンポーネントたちのいくつかをインスタンス化でき、等々となります。


2: お断り: 私たちはグローバルUNOサービスを作成します


Hypothesizer 7
全てのUNOサービスグローバルUNOサービスだというわけではありませんが、UNOサービスを作成することについて私が話すとき、私は、グローバルUNOサービスを作成することについて話します。

Objector 62A
なぜだ?私は非グローバルUNOサービスが欲しいのだ。

Hypothesizer 7
本当ですか、サー?

Objector 62A
少なくとも、私はそう言い張れる。

Hypothesizer 7
どのような目的で、そのような非グローバルUNOサービスを所望されるのですか、もしも、お聞きしてよろしければ?

Objector 62A
. . .聞いてよろしくない。

Hypothesizer 7
なるほど。 . . . 私がここで申し上げるのは、非グローバルUNOサービスを作成する必要を感じたことは1度もなく、それを感じることになるとは全く予期しないということです。

Objector 62A
君がどう感じるかなど私にはどうでもよい。

Hypothesizer 7
ごもっともです。私がここで言えるのは、それを当該UNOサービス群マネージャーに恒久的に登録できるとは推測しないが、ダイナミックにそれを行なうことはできるかもしれないということです、そのUNOサービス群マネージャーに実装されたUNOインターフェイスを通して。


3: UNOサービスを作成する



3-1: 当該UNOコンポーネントを用意する


Hypothesizer 7
UNOコンポーネントは、UNOサービスとして登録されるためには、ある一定の条件を満たしていなければなりません。

具体的には、それは、2つのUNOインターフェイス、'com.sun.star.lang.XServiceInfo'と'com.sun.star.lang.XInitialization'を実装していなければなりません、以下のように。

@Java ソースコード
package theBiasPlanet.testUnoExtension.unoComponents;

import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import com.sun.star.lang.XInitialization;
import com.sun.star.lang.XServiceInfo;
import com.sun.star.lib.uno.helper.WeakBase;
import com.sun.star.uno.XComponentContext;
// # Add necessary classes and interfaces Start
import theBiasPlanet.unoDatumTypes.tests.XTest;
// # Add necessary classes and interfaces End

public class TestUnoComponentA extends WeakBase implements XServiceInfo, XInitialization, XTest {
	private static final Set <String> c_unoServiceNames = new HashSet <String> ();
	private static final Class c_thisClass = new Object () { }.getClass ().getEnclosingClass ();
	private XComponentContext i_remoteUnoObjectsContextInXComponentContext;
	private static final String [] c_unoServiceNamesArray;
	// # Add member variables Start
	private String i_message;
	// # Add member variables End
	
	@Override
	public final void initialize (Object [] a_arguments) throws com.sun.star.uno.Exception {
		// # Write the initialization Start
		if (a_arguments.length > 0 && a_arguments [0] instanceof String) {
			i_message = (String) (a_arguments [0]);
		}
		else {
			i_message = ". . .";
		}
		// # Write the initialization End
	}
	
	@Override
	public String getImplementationName () {
		return c_thisClass.getName ();
	}
	
	@Override
	public final boolean supportsService (String a_serviceName) {
		return c_unoServiceNames.contains (a_serviceName);
	}
	
	@Override
	public final String [] getSupportedServiceNames () {
		return c_unoServiceNamesArray;
	}
	
	// # Add methods of the implemented user UNO interfaces Start
	@Override
	public String test1 (String a_name) {
		return String.format ("%s, %s", i_message, a_name);
	}
	// # Add methods of the implemented user UNO interfaces End
	
	static {
		// # Add service names Start
		c_unoServiceNames.add ("theBiasPlanet.test.TestUnoService");
		// # Add service names End
		c_unoServiceNamesArray = new String [c_unoServiceNames.size ()];
		int l_unoServiceNameIndex = 0;
		for (String l_unoServiceName: c_unoServiceNames) {
			c_unoServiceNamesArray [l_unoServiceNameIndex] = l_unoServiceName;
			l_unoServiceNameIndex ++;
		}
	}
	
	public static void setThisClassToGlobalUnoServicesProvider (Map <String, Map <Class <?>, Set <String>>> a_implementationClassNameToImplementationClassToUnoServiceNamesMapMap) {
		HashMap <Class <?>, Set <String>> l_implementationClassToUnoServiceNamesMap = new HashMap <Class <?>, Set <String>> ();
		l_implementationClassToUnoServiceNamesMap.put (c_thisClass, c_unoServiceNames);
		a_implementationClassNameToImplementationClassToUnoServiceNamesMapMap.put (c_thisClass.getName (), l_implementationClassToUnoServiceNamesMap);
	}
	
	public TestUnoComponentA (XComponentContext a_unoObjectsContextInXComponentContext) throws IllegalArgumentException {
		i_remoteUnoObjectsContextInXComponentContext = a_unoObjectsContextInXComponentContext;
	}
	
	// # Add other member methods Start
	// # Add other member methods End
}

Objector 62A
. . . うむ?ふーむ . . .

Hypothesizer 7
勿論、上記のとおりに実装される必要はありません: それらの「@Override」でアノテートされたメソッド群を何らかの形で適切に実装される必要があるということです。

Objector 62A
「何らかの形で適切に」 . . .

Hypothesizer 7
UNOサービス名群および実装クラスという情報を、私は、「c_unoServiceNames 」、「c_unoServiceNamesArray」、「c_thisClass」に集中管理します、なぜなら、そのような情報を複数メソッドに散らばらせたくないので、しかし、もしも、あなたはその情報を散らばせたいというのであれば、そうおできになります。

ところで、その"setThisClassToGlobalUnoServicesProvider (Map <String, Map <Class <?>, Set <String>>> a_implementationClassNameToImplementationClassToUnoServiceNamesMapMap)"メソッドは、次に作成されるグローバルUNOサービス群プロバイダによって使われるものであり、そのメソッドをそのとおりにお持ちになる必要はありません、もしも、グローバルUNOサービス群プロバイダに、それがしなければならないことをさせられるのであれば。

Objector 62A
そのメソッドは何をしているのだ?

Hypothesizer 7
UNOコンポーネント

3-2: グローバルUNOサービス群プロバイダを作成する


Hypothesizer 7
私たちは、グローバルUNOサービス群プロバイダを作成しなければなりません。

Objector 62B
何それ?

Hypothesizer 7
それは、2つの必須スタティックメソッド、'XSingleComponentFactory __getComponentFactory (String a_implementationName)'および'boolean __writeRegistryServiceInfo (XRegistryKey a_registryKey)'を持つクラスであり、UNOサービス群が登録され、インスタンス化されるために必要です。

Objector 62B
そんなの面倒くさい!それらの2メソッドをUNOコンポーネントに入れられないの?

Hypothesizer 7
ああ、いいご質問です。できなくはありませんが、複数のUNOコンポーネントがあるかもしれないのに、どのUNOコンポーネントがその2メソッドを持つべきでしょうか?勿論、恣意的に1つ選ぶことはできますが、構造がアンリーズナブルに非対称になるでしょう。

Objector 62B
でも、私は1つのUNOコンポーネントしか持ってないもの。

Hypothesizer 7
UNOコンポーネントUNOコンポーネント
Objector 62B
別に構わないけど。

Hypothesizer 7
勿論、選択はあなたのものですが、ひと時の省力化のために物事を歪めることは私は全く是認しません。

とにかくも、以下は、グローバルUNOサービス群プロバイダの例です。

@Java ソースコード
// # Change the package name
package theBiasPlanet.testUnoExtension.unoExtension;

import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.registry.XRegistryKey;
import theBiasPlanet.unoUtilities.servicesHandling.GlobalUnoServicesProviderUtility;
// # Add the UNO component classes Start
import theBiasPlanet.testUnoExtension.unoComponents.TestUnoComponentA;
// # Add the UNO component classes End

// # Change the class name
public class TestUnoExtensionGlobalUnoServicesProvider {
	private static final Map <String, Map <Class <?>, Set <String>>> c_implementationClassNameToImplementationClassToUnoServiceNamesMapMap = new HashMap <String, Map <Class <?>, Set <String>>> ();
	static {
		// # Add implementation classes Start
		TestUnoComponentA.setThisClassToGlobalUnoServicesProvider (c_implementationClassNameToImplementationClassToUnoServiceNamesMapMap);
		// # Add implementation classes End
	}
	
	public static XSingleComponentFactory __getComponentFactory (String a_implementationName) {
		return GlobalUnoServicesProviderUtility.getUnoServiceInstancesFactory (c_implementationClassNameToImplementationClassToUnoServiceNamesMapMap, a_implementationName);
	}
	
	public static boolean __writeRegistryServiceInfo (XRegistryKey a_registryKey) {
		return GlobalUnoServicesProviderUtility.writeGlobalUnoServicesInformationToRegistry (c_implementationClassNameToImplementationClassToUnoServiceNamesMapMap, a_registryKey);
	}
}

@Java ソースコード
package theBiasPlanet.unoUtilities.servicesHandling;

import java.util.Map;
import java.util.Set;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.registry.XRegistryKey;
import com.sun.star.lib.uno.helper.Factory;

public class GlobalUnoServicesProviderUtilityOverwritten {
	public static XSingleComponentFactory getUnoServiceInstancesFactory (Map <String, Map <Class <?>, Set <String>>> a_unoComponentClassNameToUnoComponentClassToUnoServiceNamesMapMap, String a_unoComponentClassName) {
		XSingleComponentFactory l_globalUnoServiceInstancesFactory = null;
		Map <Class <?>, Set <String>> l_unoComponentClassToUnoServiceNamesMap = a_unoComponentClassNameToUnoComponentClassToUnoServiceNamesMapMap.get (a_unoComponentClassName);
		if (l_unoComponentClassToUnoServiceNamesMap != null) {
			Map.Entry <Class <?>, Set <String>> l_unoComponentClassToUnoServiceNamesMapEntry = null;
			for (Map.Entry <Class <?>, Set <String>> l_mapEntry: l_unoComponentClassToUnoServiceNamesMap.entrySet ()) {
				l_unoComponentClassToUnoServiceNamesMapEntry = l_mapEntry;
				break;
			}
			Set <String> l_unoServiceNames =  l_unoComponentClassToUnoServiceNamesMapEntry.getValue ();
			String l_unoServieNamesArray [] = new String [l_unoServiceNames.size ()];
			int l_unoServieNameIndex = 0;
			for (String l_unoServieName: l_unoServiceNames) {
				l_unoServieNamesArray [l_unoServieNameIndex] = l_unoServieName;
				l_unoServieNameIndex ++;
			}
			l_globalUnoServiceInstancesFactory = Factory.createComponentFactory (l_unoComponentClassToUnoServiceNamesMapEntry.getKey (), l_unoServieNamesArray);
		}
		return l_globalUnoServiceInstancesFactory;
	}
	
	public static boolean writeGlobalUnoServicesInformationToRegistry (Map <String, Map <Class <?>, Set <String>>> a_unoComponentClassNameToUnoComponentClassToUnoServiceNamesMapMap, XRegistryKey a_registryKeyInXRegistryKey) {
		boolean l_returnStatus = false;
		for (Map.Entry <String,  Map <Class <?>, Set <String>>> l_unoComponentClassNameToUnoComponentClassToUnoServiceNamesMapMapEntry: a_unoComponentClassNameToUnoComponentClassToUnoServiceNamesMapMap.entrySet ()) {
			Map.Entry <Class <?>, Set <String>> l_unoComponentClassToUnoServiceNamesMapEntry = null;
			for (Map.Entry <Class <?>, Set <String>> l_mapEntry: l_unoComponentClassNameToUnoComponentClassToUnoServiceNamesMapMapEntry.getValue ().entrySet ()) {
				l_unoComponentClassToUnoServiceNamesMapEntry = l_mapEntry;
				break;
			}
			Set <String> l_unoServiceNames = l_unoComponentClassToUnoServiceNamesMapEntry.getValue ();
			String l_unoServieNamesArray [] = new String [l_unoServiceNames.size ()];
			int l_unoServieNameIndex = 0;
			for (String l_unoServieName: l_unoServiceNames) {
				l_unoServieNamesArray [l_unoServieNameIndex] = l_unoServieName;
				l_unoServieNameIndex ++;
			}
			l_returnStatus = Factory.writeRegistryServiceInfo (l_unoComponentClassNameToUnoComponentClassToUnoServiceNamesMapMapEntry.getKey (), l_unoServieNamesArray, a_registryKeyInXRegistryKey);
			if (!l_returnStatus) {
				break;
			}
		}
		return l_returnStatus;
	}
}

注意として、私はそのユーティリティクラス、「GlobalUnoServicesProviderUtilityOverwritten」を用意しましたが、その理由は、当該コードをグローバルUNOサービス群プロバイダ毎に書き込むのは不経済だろうからで、もしもあなたがお望みであれば、勿論、あなたは不経済にできます。

私は、その「c_implementationClassNameToImplementationClassToUnoServiceNamesMapMap」を、必要な情報を集中管理するために持っており、その情報は、当該UNOコンポーネント(複数かもしれない)から注入されます、先程言及した「setThisClassToGlobalUnoServicesProvider」メソッドを使って。

Objector 62A
. . . そのコードは結構長いが、プロバイダ毎に変更しなければならないことはかなり少ないと見たが。

Hypothesizer 7
そのとおりです。

Objector 62A
何とかもっと上手くできんのかね、スーパークラスを持つとか何とか?その2つのメソッドを何度も何度も定義するというのはバカらしく思われる。

Hypothesizer 7
ごもっともですが、私は方法を知りません: スーパークラスを持つというのは上手くいきません、なぜならそれらはスタティックメソッドなので。

Objector 62A
いかないかな?

Hypothesizer 7
いかないと思います。

Objector 62A
なぜ?

Hypothesizer 7
えーと、ご自分で試みられればご理解されるでしょう。


3-3: Jarマニフェストを作成する


Hypothesizer 7
必要なJavaクラス群をJarファイルにアーカイブしますが、このJarファイルはJarマニフェストを必要とします。

Objector 62B
Jarファイルが必ずJarマニフェストを必要とするわけじゃないでしょ?

Hypothesizer 7
グローバルUNOサービス群プロバイダを格納したJarファイルはどれもそれを必要とします。

Objector 62B
ふーむ。

Hypothesizer 7
以下がそのJarマニフェストです。

@MANIFEST.MF ソースコード
Comment01: # Change the class name
RegistrationClassName: theBiasPlanet.testUnoExtension.unoExtension.TestUnoExtensionGlobalUnoServicesProvider
UNO-Type-Path: <>

Objector 62A
おお、そうやって、グローバルUNOサービス群プロバイダはLibreOfficeに認識されるのか。


3-4: 拡張機能コンフィグレーションファイルを作成する


Hypothesizer 7
私たちは、当該UNOサービスをLibreOfficeまたはApache OpenOffice拡張機能を介して登録しようとしているおり、そのためにはあるコンフィグレーションファイルを必要とします。

Objector 62B
「拡張機能」?私が作れると誰が言ったの?

Hypothesizer 7
その知識は前提条件だと、「開始コンテキスト」で申し上げました。

Objector 62B
. . . 誰がそんなもの読むの?

Hypothesizer 7
どうかお読みください。

Objector 62B
. . .

Hypothesizer 7
とにかく、それは、いくつかコンフィグレーションファイルを作成し、内容物一式をZIPファイルにアーカイブするだけの容易なものです

Objector 62B
あらそう?

Hypothesizer 7
とにかく、私たちは、「UnoServiceComponents.xml」ファイルを、以下のように作ります。

@XML ソースコード
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://openoffice.org/2010/uno-components">
	<!-- # Change the jar file uri -->
	<component loader="com.sun.star.loader.Java2" uri="theBiasPlanet.test.unoExtension.jar">
		<!-- # Add service implementation class names -->
		<implementation name="theBiasPlanet.testUnoExtension.unoComponents.TestUnoComponentA">
			<!-- # Add service names -->
			<service name="theBiasPlanet.test.TestUnoService"/>
		</implementation>
	</component>
</components>

Objector 62A
. . . その「component」ノードは、Jarファイルに対応しているのか?

Hypothesizer 7
はい、「component」が締りのないオフィシャル用語体系にて濫用されているまた1つの事例です: 「component」は何ものをも意味する可能性があり、今の場合はJarファイルです。


3-5: 余談: 必要ではないものたち


Objector 62B
サービスIDLファイルを作るのを君は忘れたようだが。

Hypothesizer 7
実のところ、忘れたというよりは、それを作成する意図が全くないのです。

Objector 62B
はあ?必要だろう?

Hypothesizer 7
いいえ、あなたが指しているのが締りのないオフィシャル用語体系の中の第2の意味における「サービス」であろうが第3の意味における「サービス」であろうが、私たちはそれを特に必要としません。

Objector 62B
でも、作ったほうがいいんじゃないの?

Hypothesizer 7
えーと、第2の意味における「サービス」はかなりまずく実現されたコンセプトであり、廃棄したほうがよいです、助けになるよりは、混乱の元になるので、私の意見では。

Objector 62B
どのように「まずく実現された」の?

Hypothesizer 7
たぶん、それが「サービス」と名付けられた理由は、そのような「サービス」は元々は、対応するUNOサービスへのユーザー向けインターフェイスとして意図されていたことでしょうが、その意図は、上手く実現されていません: 「サービス」は一般的に何らのUNOサービスにも対応しませんし、あるUNOサービスは何らの「サービス」にも対応しないかもしれませんし、あるUNOサービスに対応しているように思われるある「サービス」は、そのUNOサービスを正確に代表していないかもしれません。

現実には、「サービス」は、一般的には、UNOサービスではなく、またUNOサービスに対応してさえいないのですが、その実現されていない意図にオフィシャルリファレンスドキュメントがしがみ続けるから、話が混乱するのです、私の分析では。

Objector 62B
. . . それじゃあ、「サービス」は全然意味ないってこと?

Hypothesizer 7
えーと、ドキュメント上の意義はあるかもしれませんが、その情報は特に信頼の置けるものではありません、第2の意味における「サービス」のコンテンツが、同一名のUNOサービスのそれと一致する保証はないからです。

Objector 62B
第3の意味における「サービス」はどうなの?

Hypothesizer 7
それは、ファクトリであるという実際的利得を持っているので、作られてもよいですが、特にそれが必要というわけではありません。


3-6: 拡張機能ファイルを作成する


Hypothesizer 7
それでは、拡張機能ファイルを作成します。

方法を説明する必要はありません、なぜなら、それは、ある以前の記事にて既に詳説されていますから。


3-7: 拡張機能を登録する


Hypothesizer 7
拡張機能を登録します。

それは、GUIメニューアイテム、「Tools(ツール)」 -> 「Extension Manager...(拡張機能...)」を介して行なえます。


4: UNOサービスをインスタンス化する


Hypothesizer 7
当該UNOサービスのインスタンス化は、任意のビルトイングローバルUNOサービスと同様に行なえます。

Objector 62B
. . . どうやって、具体的には?

Hypothesizer 7
例えば、以下はPythonマクロです。

@Python ソースコード
from typing import Any
from com.sun.star.script.provider import XScriptContext
from com.sun.star.lang import XMultiComponentFactory
from com.sun.star.uno import XComponentContext

XSCRIPTCONTEXT: XScriptContext

def test1 () -> None:
	l_unoObjectsContextInXComponentContext: XComponentContext = XSCRIPTCONTEXT.getComponentContext ()
	l_unoServicesManager: XMultiComponentFactory = l_unoObjectsContextInXComponentContext.getServiceManager ()
	l_testUnoService: Any = l_unoServicesManager.createInstanceWithArgumentsAndContext ("theBiasPlanet.test.TestUnoService", ["Hi"], l_unoObjectsContextInXComponentContext)
	l_testUnoService.test1 ("Sally")

Objector 62B
それじゃあ、あのJavaクラスがPythonからインスタンス化できるのね。

Hypothesizer 7
それが、UNOサービスの目的です。


参考資料


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