2018-08-13

9: 'Hi, Console Java UNO Clients', a Sample

<The previous article in this series | The table of contents of this series | The next article in this series>

Connecting your program to the LibreOffice/OpenOffice instance (or any UNO server), in order to read/write an office document or handle the instance.

Topics


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: Java programming language

The table of contents of this article


Starting Context



Target Context


  • The reader will know how to connect to any LibreOffice or Apache OpenOffice instance from his or her console Java program.
Stage Direction
Here are Hypothesizer 7, Objector 9A, and Objector 9B in front of a computer.


Orientation


Hypothesizer 7
In this article, we will get, build, execute, and understand a console Java UNO client sample.

Objector 9A
You must have misunderstood something. "Has to Connect to the LibreOffice or Apache OpenOffice Instance"? I just want to read and write Calc sheets in my Java program; I certainly don't have to connect to any LibreOffice or Apache OpenOffice instance, whatever that means.

Hypothesizer 7
Sir, . . . you certainly have to.

Objector 9A
Are you deaf? I just want to read and write Calc sheets in my Java program, I said!

Hypothesizer 7
I heard so and have understood so. . . . Sir, if you understood what UNO was, you would understand what I meant.

Objector 9A
How dare you? Are you telling me to read your damn article?

Hypothesizer 7
Not particularly. I just said that this article presupposed certain prerequisite knowledge, as most technical documents do; it does not matter where you acquire that knowledge.

Objector 9A
In fact, I don't care whether you are wrong or I am correct, only if I can read and write LibreOffice document files.

Hypothesizer 7
. . . Then, you have to connect to the LibreOffice or Apache OpenOffice instance, first.

Objector 9A
What instance? I don't have any instance.

Hypothesizer 7
You have to start one, as a UNO server.

Objector 9B
I formally complain that you didn't say that this was about LibreOffice document files, not Microsoft Office document files: I have come here because I thought this was about Microsoft Office document files, in waste of my precious time.

Hypothesizer 7
Well, I did not say so because that was not the case: this is also about Microsoft Office document files . . .

Objector 9B
Huh? . . . Why didn't you say so?

Hypothesizer 7
I thought that your complaint was that I said so . . .

Objector 9B
. . . Then, why are you talking about LibreOffice?

Hypothesizer 7
Madam, if you read a previous article, you will understand why.

Objector 9B
I won't read such a thing!

Hypothesizer 7
I see.


Main Body


1: Getting and Building the Console Java UNO Client Sample


Hypothesizer 7
Here is a console Java UNO client sample. How to build any sample project of this series is described in a previous article.

Objector 9A
As I have expanded the ZIP file, there are three directories . . .

Hypothesizer 7
'hiConsoleJavaUnoClients' is the project directory of the sample program; the other two directories are of projects-common projects.

Objector 9A
So, I need only the sample project.

Hypothesizer 7
. . . The other two projects are also included because they are necessary too. However, they will be automatically built when you build the sample project.

Objector 9A
So, how can I build the sample project?

Hypothesizer 7
. . . Please read a previous article.

Objector 9B
It doesn't succeed . . .

Hypothesizer 7
Have you built your development environment as a previous article (here for Linux and here for Windows) says?

Objector 9B
I certainly haven't seen such an article.

Hypothesizer 7
I can just introduce those articles (here for Linux and here for Windows); especially note that any space is not supposed to be in any directory name.

Objector 9B
Huh? Of course, there are spaces!

Hypothesizer 7
They are not supposed to be, by the build scripts.

Objector 9B
That is tyranny!

Hypothesizer 7
That is more an omission of fuss over dispensable things, for me.

Objector 9B
I would have to reinstall things!

Hypothesizer 7
Ah, you are right: I, who have always avoided any space in any directory name and in any file name, did not imagine such a situation. However, that requirement stands, at least for now.

Objector 9B
. . .

Stage Direction
Hypothesizer 7 opens a terminal and builds the sample project.


2: Executing the Sample Program


Hypothesizer 7
Before we execute the sample program, we have to have started a LibreOffice or Apache OpenOffice instance so that it accepts connections from clients, as we can know how in a previous article.

Objector 9B
"a previous article", again?

Hypothesizer 7
That is an indispensable preparation, and I have made it a separate article because it has to be referred to from multiple articles.

Stage Direction
Objector 9A and Objector 9B grudgingly read the cited article.

Objector 9B
. . . Now what?

Stage Direction
Hypothesizer 7 starts a LibreOffice instance with the port number 2002.

Hypothesizer 7
We can execute the sample program like this with the current directory positioned at the sample project directory and the file URL adjusted to our environment.

@bash or cmd Source Code
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"

Stage Direction
Hypothesizer 7 executes the command in the terminal with the current directory positioned at the sample project directory. A OpenDocument Text file is opened and then closed immediately.

Objector 9A
Is that it?

Hypothesizer 7
Actually, that is.

Objector 9A
A poor sample . . .

Hypothesizer 7
. . . This sample concentrates on connecting to the UNO server; 'richer' samples will come in future articles.

Objector 9A
Whatever.

Hypothesizer 7
We can just change the host name from 'localhost' if the LibreOffice or Apache OpenOffice instance is in a remote host, if some firewalls do not block the communication, of course.

Note that in Windows, '${HOME}' is like '/E:/home/tanichida'.


3: Understanding the Sample Program



3-1: Understanding the Projects Structure


Hypothesizer 7
As you have noticed, there are three projects: 'coreUtilitiesToBeDisclosed', 'unoUtilitiesToBeDisclosed', and 'hiConsoleJavaUnoClients'. 'coreUtilitiesToBeDisclosed' contains utility code pieces that are supposed to be commonly used in all kinds of projects; 'unoUtilitiesToBeDisclosed' contains utility code pieces that are supposed to be commonly used in UNO program projects; 'hiConsoleJavaUnoClients' is the sample project.

Objector 9A
As I look into the source code of the sample program, there is not much to be seen in it . . .

Hypothesizer 7
In order to learn how to make the connection, we have to look into the 'unoUtilitiesToBeDisclosed' project.

Objector 9A
I can just use the utility classes as they are, right?

Hypothesizer 7
You can, if you are satisfied with those utility classes.

Objector 9A
I'm not particularly satisfied, but also I'm not fussy about making connections: in fact, if I can get the connection all right, what else can practically matter to me?

Hypothesizer 7
If you are going to use the utility classes as they are, first, I will tell you what matter for you in order to go further.


3-2: What Matter in Order to Go Beyond Connecting to the UNO Server Using the Utility Projects


Hypothesizer 7
After we have got a connection, we need a UNO objects context of the UNO server.

Objector 9A
Do I?

Hypothesizer 7
You definitely do. You have connected to the UNO server because you want to handle UNO objects that reside in the UNO server.

Objector 9A
So what?

Hypothesizer 7
The UNO objects context is the point starting from which we can get references to UNO objects one by one, as we learn how in a future article.

Objector 9A
Whatever.

Hypothesizer 7
We can get the UNO objects context from the 'theBiasPlanet.unoUtilities.connectionsHandling.UnoConnection' instance with the 'getRemoteObjectsContext' method.

By the way, the local UNO process (that is to say the sample program process) has its own UNO objects context, and we can get it from the 'theBiasPlanet.unoUtilities.programsHandling.UnoProcessEnvironment' class instance with the 'getLocalObjectsContext' method, but probably, you will have no opportunity to use it yourself in your UNO client (it is used in utility code pieces in order to establish the connection and the UNO bridge, as will be shown below).


3-3: The Wrapper of UNO Objects Context


Hypothesizer 7
The 'theBiasPlanet.unoUtilities.connectionsHandling.UnoObjectsContext' class in the 'unoUtilitiesToBeDisclosed' project is a wrapper of UNO objects context. I have created the wrapper because otherwise we cannot add properties into any UNO objects context.

Objector 9B
Why do I have to add properties into a UNO objects context?

Hypothesizer 7
You do not particularly have to, but propagating such added properties is the purpose of UNO objects context; so, I thought that the wrapper was due.

Objector 9B
Hmm . . .

Hypothesizer 7
As the wrapper implements 'com.sun.star.uno.XComponentContext', we can pass around the wrapper wherever a 'com.sun.star.uno.XComponentContext' is required.


3-4: Creating a Local UNO Objects Context (Bootstrapping)


Hypothesizer 7
When we connect to the UNO server, we do so using a local (that is to say 'residing in the UNO client') UNO object.

Objector 9B
I don't mind, but an obvious question is how I can get that local UNO object.

Hypothesizer 7
As I said, we get references to UNO objects one by one, starting from a UNO objects context.

Objector 9B
An obvious question is how I can get that UNO objects context.

Hypothesizer 7
That UNO objects context is, of course, a local UNO objects context, and we create it from scratch (creating it is called 'bootstrapping').

Objector 9B
That's reasonable.

Hypothesizer 7
In fact, we can create the local UNO objects context like this, as can be seen in the constructor of the 'theBiasPlanet.unoUtilities.programsHandling.UnoProcessEnvironment' class.

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

XComponentContext l_originalLocalObjectsContext = Bootstrap.createInitialComponentContext (null);


3-5: Connecting to the UNO Server


Hypothesizer 7
As now we have the local UNO objects context, we can get a 'com.sun.star.connection.XConnector' instance, the UNO object we use in order to connect to the UNO server, as a 'com.sun.star.connection.Connector' UNO service instance, as can be seen in the 'connect' method of the 'theBiasPlanet.unoUtilities.connectionsHandling.UnoConnectionConnector' class.

Objector 9B
'i_localObjectsContext.getServiceInstance'?

Hypothesizer 7
That is a utility class method that gets a UNO service instance via the global UNO services manager.

Objector 9B
Hmm . . .

Hypothesizer 7
And we can connect to the UNO server by calling the 'connect' method of the 'com.sun.star.connection.XConnector' UNO interface with the connection string as this: 'socket,host=localhost,port=2002,tcpNoDelay=1'.

Objector 9B
Ah, the meaning of the connection string looks obvious, except 'tcpNoDelay=1'.

Hypothesizer 7
Refer to this, and '1' means 'on'.

Objector 9B
. . . I see.

Hypothesizer 7
There are some things left to be done: to create a UNO bridge and get a UNO objects context of the UNO server. We do that like this, as can be seen in the 'initialize' and 'setupConnection' methods of the 'theBiasPlanet.unoUtilities.connectionsHandling.UnoConnectionsFactory' class .

Get a 'com.sun.star.bridge.XBridgeFactory' instance as a 'com.sun.star.bridge.BridgeFactory' UNO service instance; create a UNO bridge by calling the 'createBridge' method of the 'com.sun.star.bridge.XBridgeFactory' UNO interface, specifying the UNO bridge name, the protocol ('urp'), the connection, and an initial UNO objects provider and get the UNO bridge as a 'com.sun.star.bridge.XBridge' instance; get a UNO objects context of the UNO server by calling the 'getInstance' method of the 'com.sun.star.bridge.XBridge' UNO interface, specifying an initial UNO object name, 'StarOffice.ComponentContext'.

Objector 9B
"an initial UNO objects provider"? "an initial UNO object name"?

Hypothesizer 7
After we have established a UNO bridge, we can get initial UNO objects from the other side of the UNO bridge.

Objector 9B
What , the hell, are initial UNO objects?

Hypothesizer 7
In order to get a UNO object, we have to get it from another UNO object, which we have to get from another UNO object . . .; so, there must be an initial UNO object from which we start.

Objector 9B
Um? Is there a single initial UNO object, or are there multiple initial UNO objects?

Hypothesizer 7
How many initial UNO objects and what UNO objects the UNO bridge end offers depends on the UNO bridge end.

Objector 9B
Hmm . . .

Hypothesizer 7
In fact, we are offering the local UNO objects context to the UNO server by specifying the initial UNO objects provider with 'theBiasPlanet.UnoObjectsContext' (the value of 'UnoDefaultValuesConstantsGroup.c_initiallyOfferedUnoObjectName') being the name of the sole initially offered UNO object.

Objector 9B
Does that mean, the LibreOffice instance is offering 'StarOffice.ComponentContext' the same way?

Hypothesizer 7
Exactly.

Objector 9B
So, 'StarOffice.ComponentContext' isn't a UNO service name, but a name just specified in the initial UNO objects provider in the LibreOffice program?

Hypothesizer 7
Exactly.

By the way, although official samples use 'StarOffice.ServiceManager' instead of 'StarOffice.ComponentContext', I do not think that is a good practice.

Objector 9B
"StarOffice.ServiceManager"?

Hypothesizer 7
The LibreOffice or Apache OpenOffice instance offers also the global UNO services manager as an initial UNO object, with that name.

Objector 9B
Why is that not a good practice?

Hypothesizer 7
Because in the new model of UNO, UNO objects contexts with their properties are meant to be passed around, not the global UNO services manager.

Objector 9B
Is there any practical harm?

Hypothesizer 7
I do not think there is if the UNO server is a LibreOffice or Apache OpenOffice instance: we can get the default UNO objects context from the global UNO services manager, and LibreOffice and Apache OpenOffice seem to give us the default UNO objects context anyway. However, theoretically, a UNO server can give us a different UNO objects context with different properties, perhaps, depending on who we are (for example, reflected in the initial UNO object name we specify).


3-6: Cutting the Connection


Hypothesizer 7
We can cut the connection in this procedure, as can be seen in the 'disconnect' method of the 'theBiasPlanet.unoUtilities.connectionsHandling.UnoConnection' class.

Call the 'dispose' method of the 'com.sun.star.lang.XComponent' UNO interface of the UNO bridge UNO object.


4: Although There Is a Simpler Way of Connecting to an Office Instance . . .


Hypothesizer 7
Actually, there is a simpler way of connecting to an office instance, but that way has some limitations, which are critical in certain cases.

First, in the way, the UNO client can connect only to an office instance that is started by the UNO client operating system user in the UNO client computer.

That is critical because when the UNO client is a serious server program, the permissions required only by the office instance cannot be confined to the office instance, but have to be extended to the UNO client, and the workload of the office instance cannot be separated into a different computer.

Objector 9A
I can just set up a relay Web application or something in the remote computer where the office instance resides and let my original program access the office instance via the relay.

Hypothesizer 7
Certainly, there is such a standby, but that would usually cause you more work and more overhead than directly connecting your program to the office instance.

Objector 9A
Would that?

Hypothesizer 7
I think so because your way requires your implementing the proxy interface for every access to the office instance, compared with just writing more lines for only the connection establishment.

Objector 9A
Hmm . . .

Hypothesizer 7
Second, in the simpler way, the UNO client cannot receive any disconnection event.

Objector 9A
Will that be a problem?

Hypothesizer 7
That may not be desirable in certain cases. For example, when the UNO client is something that waits for the user's some inputs, the user will be happier to be notified immediately when the connection is severed than to be left inputting many data that are destined to be in vain because the connection has been already severed.

Objector 9A
Can't I just reestablish a connection and let the user proceed seamlessly?

Hypothesizer 7
That is not impossible, but will be difficult in many cases because just reestablishing a connection is not enough; you have to reconstruct the states of the office instance and the UNO client.

Objector 9A
What do you mean by "the states"?

Hypothesizer 7
For example, before the office instance aborted, it may have had a opened document with the cursor placed at a specific position while the UNO client may have had some UNO proxies.

Objector 9A
Ah . . .

Hypothesizer 7
Third, the simpler way cannot be expanded to creating any UNO server.

Objector 9A
Should I want to create one?

Hypothesizer 7
I do not say that you should, but if you want . . .

Objector 9A
I don't understand why I would ever want.

Hypothesizer 7
For example, you may want to delegate some work to a UNO server from one of your UNO extensions.

Objector 9A
Can't I just delegate such work via HTTP or something?

Hypothesizer 7
That may be fine in many cases, but that way, you may have to hassle in letting the server access the necessary data.

Objector 9A
What do you mean?

Hypothesizer 7
While the UNO extension handles some UNO related data in many cases, if the server needs to access some of such data, being able to let the server handle such data as they are may be more handy than transforming each of such data into a format that can be passed via HTTP and passing the transformed datum to-and-fro.

Objector 9A
Hmm . . .


5: The Conclusion and Beyond


Hypothesizer 7
Now, we can connect to any UNO server from our console Java UNO client.

The reason why I have specified that the sample is a 'console' client is that a GUI Java UNO client sample will come in the next article.

The GUI Java UNO client might not seem to have to be different from the console Java UNO client UNO-wise, but there is a difference: as the UNO client is supposed to be up for long duration, it is more appropriate to detect connection cuts caused by the UNO server or the network.

And we will look at UNO clients in other programming languages (C++ and C#) in future articles.


References


<The previous article in this series | The table of contents of this series | The next article in this series>