2017-10-15

1: The Preface of the Series, 'How to Use UNO (Handle LibreOffice or Apache OpenOffice Documents) in External Java Programs'

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

Main Body START

This Series Is a Complement to the Main Series, 'To Develop UNO Extensions (LibreOffice Extensions or Apache OpenOffice Extensions)', for Applying Descriptions of the Main Series to Using UNO in External Java Programs

About: UNO (Universal Network Objects)

About: LibreOffice

About: Apache OpenOffice

About: Java Programming Language

The Purpose of This Series

-Hypothesizer

There is already a series, 'To Develop UNO Extensions (LibreOffice Extensions or Apache OpenOffice Extensions)', which is about developing UNO extensions that consist of Java UNO components, and most of its descriptions also apply to developing external UNO programs because they are about using UNO, which is the same whether we use it in UNO extensions or external programs.

-Rebutter

What do you mean by 'external programs'?

-Hypothesizer

By 'external programs', I mean programs that run outside any LibreOffice program, typically ordinary programs that start with the 'main' methods (I'm talking only about Java programs).

-Rebutter

That isn't an accurate explanation of 'external programs', is it?

-Hypothesizer

Well, . . . it isn't. . . . Hmm, a simple explanation like "External programs are standalone programs." will be liked and will be thought to be an explanation that is 'easy to understand'; explaining more accurately is likely to be hated, or more likely, just to be thrown out immediately, by most people.

-Rebutter

I don't mind.

-Hypothesizer

Well, as I'm talking to you, not to most people, it certainly is fine if you don't mind. . . .

To be exact, being a external program is being any environment to which the desired component context (the 'component context' is a handle to a UNO environment) is not just given.

-Rebutter

Ah-ha . . .

-Hypothesizer

Do you understand?

-Rebutter

Actually, I do.

-Hypothesizer

That's good. To give a supplementary explanation, once we get the component context of the UNO environment that we desire to handle, we can do whatever allowed us to do to the UNO environment; so, how to get the component context is the issue.

-Rebutter

For a UNO extension, the component context of the UNO environment into which the UNO extension has been registered is passed into the UNO extension, but for an 'ordinary' Java program, any component context won't be passed into it miraculously, perhaps, from Heaven.

-Hypothesizer

So, we have to go to get the desired component context ourselves. In other words, if a program is in that situation, that program is an external program, whether it is an 'ordinary' program, a web application, an applet, or whatever, even a UNO extension.

-Rebutter

If the UNO extension wants to access a UNO environment that isn't the UNO environment into which the UNO extension has been registered, it is an external program to the desired UNO environment.

-Hypothesizer

If the truth be told, just saying 'external' is nonsense: I would say, "External to what?"

-Rebutter

Obviously, what we mean is 'external' to the desired UNO environment.

-Hypothesizer

To repeat, all the descriptions of the main series about after the component context is obtained are completely applicable to any 'external program'. For example, the descriptions about reading from and writing to spread sheet cells are completely applicable to external UNO programs: just use the appropriate component context.

As we don't want to make duplicate descriptions, in this series, we will describe only matters that are solely about external UNO programs.

-Rebutter

OK.

How to Build the Environment for Developing External UNO Programs

-Hypothesizer

First, we have to build the environment for developing external UNO programs. As the environment for developing external UNO programs is almost the same with that for developing UNO extensions, see the corresponding articles (here for Linux and here for Windows) in the main series.

-Rebutter

What are the differences between the environment for developing external UNO programs and the environment for developing UNO extensions?

-Hypothesizer

The environment for developing external UNO programs is a sub set of the environment for developing UNO extensions: there are some things unnecessary for developing external UNO programs.

For Linux, wmctrl is unnecessary because we don't need to shutdown LibreOffice program instances. And for both Linux and Windows, LibreOffice SDK isn't necessarily necessary if we develop only UNO clients (not servers) although it is handy because it includes some documents.

-Rebutter

We needed it for developing UNO extensions because it includes commands like 'idlc', which compiles UNOIDL files (see here to know what UNOIDL files are).

-Hypothesizer

Yes. We wouldn't have to create UNO components (see here to know what UNO components are) although we can, if we are going to create only UNO clients.

-Rebutter

So, the Jar files we use are included in LibreOffice itself, not in the SDK?

-Hypothesizer

Yes.

And if we want to use other building tools like Maven, make, Eclipse, NetBeans, or whatever, of course, we can. For developing external UNO clients, it's just a matter of including necessary Jar files in the classpath.

-Rebutter

Although we have implemented the functionality of compiling UNOIDL files, creating the UNO data types merged registry file, creating the UNO extension file, registering the UNO extension into LibreOffice, and restarting the LibreOffice program instance, into our Gradle build scripts and Ant build files, that functionality isn't necessary for developing external UNO clients.

Although There Is A Simple Way to Get the Component Context of the UNO environment of a Local Host LibreOffice Program Instance, . . .

-Hypothesizer

For a case in which we just want to get the component context of the UNO environment of a local host LibreOffice program instance, there is a simple way like this (just make sure that as this uses JNI, java.library.path must be properly set at run time, or an exception, "java.lang.UnsatisfiedLinkError: no jpipe in java.library.path", will occur).

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

  XComponentContext l_remoteComponentContextInXComponentContext = Bootstrap.bootstrap ();
-Rebutter

That's it?

-Hypothesizer

That's it. Now, we get the component context, and can apply most of the descriptions in the series, 'To Develop UNO Extensions (LibreOffice Extensions or Apache OpenOffice Extensions)', to our external UNO programs, just by using the component context.

-Rebutter

But the LibreOffice program instance to be connected to must be on the local host, meaning being in the same computer with our external UNO programs, right?

-Hypothesizer

Yup. Actually, that method uses a named pipe to connect to the LibreOffice program instance.

-Rebutter

How will the name be determined?

-Hypothesizer

The method creates a random name every time called, registers the name to the LibreOffice program instance (if no LibreOffice program instance is up, the method will start one).

-Rebutter

So, if we run our external UNO program many times while the LibreOffice program instance keeps up, will many names be accumulated in the LibreOffice program instance?

-Hypothesizer

That seems to be the case. Certainly, there will be a limit on the number of named pipes, but . . .

-Rebutter

Well, practically, there may not be any harm in most cases, but it seems a waste.

-Hypothesizer

Anyway, if one is happy with that way, he or she doesn't need to read along this series further: the rest to be known is (or will be) written in the main series, 'To Develop UNO Extensions (LibreOffice Extensions or Apache OpenOffice Extensions)'.

This Is a More Flexible Way to Get the Component Context of the UNO environment of any LibreOffice Program Instance (Whether on the Local Host or on a Remote Host) or Whatever

-Hypothesizer

If we want to connect to a remote host LibreOffice program instance, want to detect disconnections, or want to disclose UNO objects from the external UNO program, we will have to use another way.

As this way can't be written in one line, I have created utility classes, which can be downloaded from here.

Deferring the lengthy lecture to future articles, we can get the component context of a LibreOffice program instance like this, where we pass 'socket,host=localhost,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext' to 'a_serverUrl'.

@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

So, we use a socket, not a named pipe, and if we want to connect to a remote host LibreOffice program instance, we can just change the 'localhost' part.

-Hypothesizer

The LibreOffice program instance has to have been started like this.

For Linux:

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

For Windows:

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

Or we can add a parameter, '--headless', if we want the LibreOffice program instance to be headless (meaning invisible, mostly).

-Rebutter

That starts a LibreOffice program instance, opening the port, '2002'. . . . What if a LibreOffice program instance is already up without the port opened?

-Hypothesizer

The exactly same command opens the port with the instance kept up.

-Rebutter

I see.

Let's Read from and Write to Spread Sheet Cells, for Example

-Hypothesizer

In fact, as the 'thebiasplanet.unoutilities.jar' (a Jar file built from one of the downloaded projects) includes utility classes to access spread sheets, we can read from and write to spread sheet cells like this (inserting these import directives and the subsequent code into appropriate places of the above code respectively), while we pass the URL of a spread sheets document file (the file has to have a sheet named 'Sheet1') to 'a_spreadSheetsDocumentFileUrl'.

@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

Those utility classes are things that gathered together what are described in some articles of the main series (especially this and several subsequent articles).

-Hypothesizer

Yes.

Actually, the test program is included in the above zip file, and we can build the utilities Jars (in fact, there are two, 'thebiasplanet.coreutilities.jar' and 'thebiasplanet.unoutilities.jar') and the test program, and run the test program like this.

1. Expand the zip file with the directories structure preserved.

2. May have to change some parameters ('LIBREOFFICE_DIRECTORY_NAME' and 'LIBREOFFICE_SDK_DIRECTORY_NAME') in 'commonBuild01.gradle' or 'commonBuild.properties' (see here).

3. On a terminal, change the current directory to the 'coreUtilitiesToDisclose', and execute 'gradle' or 'ant'.

4. Change the current directory to the 'unoUtilitiesToDisclose', and execute 'gradle' or 'ant'.

5. In 'unoUtilitiesTestToDisclose', make sure that the contents of the main method of 'Test.java' is this.

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

6. Change the current directory to the 'unoUtilitiesTestToDisclose', and execute 'gradle' or 'ant'.

7. For Gradle on Linux bash, execute this (change the '$(pwd)' part for other environments, especially '%CD%' for Windows).

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

For Ant on Linux bash, execute this (change the '$(pwd)' part for other environments, especially '%CD%' for Windows).

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

Merits of the Latter Way

-Hypothesizer

I have briefly stated merits of the latter way, but I will elaborate on them.

-Rebutter

OK.

-Hypothesizer

We can connect to a LibreOffice program instance on a remote host if the instance has opened a port.

-Rebutter

All right.

-Hypothesizer

We can register listeners of disconnection events.

Although we may not feel any necessity to register such listeners for batch-like client programs as the sample above, when we create a GUI client that stays alive for indefinite periods, detecting such events can be handy.

-Rebutter

So, when the connection is broken, perhaps because the LibreOffice program instance has been shutdown normally or abnormally, or the network has been severed, the client detects the event immediately.

-Hypothesizer

Yes.

-Rebutter

Well, it sounds slick, but I don't particularly see how that functionality is necessary.

-Hypothesizer

Well, you mean, detecting the disconnection when trying to access the remote UNO environment is enough?

-Rebutter

Yes, at least for most cases.

-Hypothesizer

That may be so. . . . We can just appropriately handle errors caused by disconnections. In fact, I haven't felt any particular inconvenience that way regarding, for example, JDBC connections. And if necessary, we can do polling.

-Rebutter

. . .

-Hypothesizer

As another merit, we can create UNO servers that disclose UNO objects.

-Rebutter

Ah-ha. So, for example, we can create a UNO server that does some intensive calculations or data accesses, and let LibreOffice program instances delegate such intensive work to the server through, perhaps, UNO extensions or Basic macros.

-Hypothesizer

Yes. Then, we don't need to do intensive work by Basic macros, which don't seem to be optimal for such work.

Where to Look at in the Main Series When Our Sole Concern Is Developing External UNO Programs

-Rebutter

Can you give me a more specific guidance of what in the main series are or are not applicable to external UNO programs?

-Hypothesizer

Well, if we are concerned with developing only external UNO clients (not servers), most of the articles about the two sample UNO extensions will be irrelevant. On the other hand, if we are also concerned with developing external UNO servers, also most of those articles are relevant because we are likely to create UNO components and register them as UNO services.

-Rebutter

What about other articles?

-Hypothesizer

They are about generally using UNO. So, They are the same for external UNO programs.

Especially, correct understanding of basic concepts of UNO will be necessary in order to use UNO at will. Without that understanding, we wouldn't be able to sufficiently understand what the UNO API documents say.

Main Body END

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