Transparent Use of Office UNO Components
Introduction
This document is about making the use of office UNO components transparent to the user.
If some client code wants to use office UNO components, then the typical use case is, that the client code is first looking for an existing office installation. If an installation is found, it is checked if the office process is already running. If no office process is running, an office process is started. After that the client code connects to the running office using remote UNO mechanisms in order to get the remote component context of that office. After this, the client code can use the remote component context to access arbitrary office UNO components. From the perspective of the client code there is no difference between local and remote components.
The idea is, that the component context is provided in a more transparent way. Especially the location of an existing office installation and the detection of a running office process should be hidden from the user.
Transparent Use from Java
The UNO component context is the root object for all UNO client applications. It must be passed to a component during its instantiation and therefore basically provides an environment for components. The component context also provides the service manager, which is used to create components. The idea is, that a bootstrap method is provided, which bootstraps the component context from a UNO installation. A simple client application may then look like:
// get the remote office component context XComponentContext xContext = com.sun.star.comp.helper.Bootstrap.bootstrap(); // get the remote office service manager XMultiComponentFactory xServiceManager = xContext.getServiceManager(); // get an instance of the remote office desktop UNO service Object desktop = xServiceManager.createInstanceWithContext( "com.sun.star.frame.Desktop", xContext ); // query the XComponentLoader interface from the desktop XComponentLoader xComponentLoader = (XComponentLoader)UnoRuntime.queryInterface( XComponentLoader.class, desktop ); // load a spreadsheet document String loadURL = "private:factory/scalc"; PropertyValue[] loadProps = new PropertyValue[0]; xComponentLoader.loadComponentFromURL( loadURL, "_blank", 0, loadProps);
One of the requirements for a Java client application is, that Java finds the
com.sun.star.comp.helper.Bootstrap
class and all the UNO types
(e.g. UNO interfaces) and other Java UNO language binding classes (e.g.
com.sun.star.uno.AnyConverter
) used by the client code. A natural
approach would be to add the UNO jar files to the Java CLASSPATH, but this
requires the knowledge of the location of a UNO installation. Other approaches
would be to use the Java extension mechanism or to deploy the jar files
containing the UNO types in the client jar file. Both of those approaches have
several drawbacks, the most important one is the problem of type conflicts, e.g.
if a deployed component adds new UNO types. We therefore decided to use a more
dynamic approach, namely to provide a customized class loader. The customized
class loader will have an extended search path, which will also include the path
to a UNO installation. The UNO installation will be auto-detected on the system
by a provided search algorithm.
Customized Class Loader
The concept is based on the requirement that every class that uses UNO types or other classes that come with a office installation gets loaded by a customized class loader. This customized class loader knows the location of a UNO installation and loads every class from a jar or class file that belongs to the office installation. That means, the customized class loader must be instantiated and initialized before the first class that uses UNO is loaded.
For convenience we will provide some tooling in the SDK, which allows to build a client jar file, which can be invoked by
java -jar MyApplication.jar
A client application created by using the SDK tooling will automatically load
the class com.sun.star.lib.loader.Loader
, which sets up the
customized class loader for loading the application class. In order to achieve
this, the SDK tooling creates a manifest file that contains the following
Main-Class
entry
Main-Class: com.sun.star.lib.loader.Loader
The customized loader needs a special entry in the manifest file that specifies the name of the class that contains the client application code:
Name: com/sun/star/lib/loader/Loader.class Application-Class: MyApplication
The implementation of com.sun.star.lib.loader.Loader.main
will
read this entry and call the main method of the application class after the
customized class loader has been created and set up properly. The SDK tooling
will take over the task to write the correct manifest entry for the application
class.
Finding a UNO installation
The location of a UNO installation can be specified by the Java system
property com.sun.star.lib.loader.unopath
. The system property can
be passed to the client application by using the -D
flag, e.g
java -Dcom.sun.star.lib.loader.unopath=/opt/OpenOffice.org/program -jar
MyApplication.jar
In addition, it is possible to specify a UNO installation by setting the
environment variable UNO_PATH
to the program directory of a UNO
installation, e.g.
setenv UNO_PATH /opt/OpenOffice.org/program
Note, that this is not working with Java 1.3.1 and Java 1.4, because environment variables are not supported in those Java versions.
If no UNO installation is specified by the user, the default UNO installation on the system is searched for. The search algorithm is platform dependent.
On the Windows platform, the UNO installation is found by reading the default
value of the key "Software\OpenOffice.org\UNO\InstallPath" from the
root key HKEY_CURRENT_USER in the Windows Registry. If this key is missing,
the key is read from the root key HKEY_LOCAL_MACHINE. One of those keys is
always written during the installation of an office. In a single user
installation the key is written to HKEY_CURRENT_USER, in a multi-user
installation of an administrator to HKEY_LOCAL_MACHINE. Note, that the default
installation is the last installed office, but with the restriction, that
HKEY_CURRENT_USER has a higher priority than HKEY_LOCAL_MACHINE. The reading
from the Windows Registry requires, that the native library unowinreg.dll is
part of the application jar file or can be found in the
java.library.path
. The SDK tooling automatically will put the
native library into the jar file containing the client application.
On the Unix/Linux platforms, the UNO installation is found from the
PATH
environment variable. Note, that for Java 1.3.1 and Java 1.4
the installation is found by using the which
command, because
environment variables are not supported with those Java versions. Both methods
require that the soffice
executable or a symbolic link is in one of
the directories listed in the PATH
environment variable. For older
versions than OOo 2.0 the above described methods may fail. In this case the UNO
installation is taken from the .sversionrc file in the user's home directory.
The default installation is the last entry in the .sversionrc file which points
to a UNO installation. Note, that there won't be a .sversionrc file with OOo 2.0
anymore.
The bootstrap method
The com.sun.star.comp.helper.Bootstrap.bootstrap()
method is
implemented in that way, that it first bootstraps a local component context by
calling the method
com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null)
.
Then it tries to establish a named pipe connection to a running
office by using the com.sun.star.bridge.UnoUrlResolver
service. If
the connection fails, an office process is started. After that, it
tries to connect to the running office again. If the connection
succeeds, it gets the remote component context, which is returned.
Note, that the office process can only be started, if the
juh.jar
file is located in the classes directory of an office
installation. If the juh.jar
file is copied to another location,
the bootstrap method fails.
Transparent Use from C++
Also for C++ a bootstrap function is provided, which bootstraps the component context from a UNO installation. An example for a simple client application shows the following code snipplet:
// get the remote office component context Reference< XComponentContext > xContext( ::cppu::bootstrap() ); // get the remote office service manager Reference< XMultiComponentFactory > xServiceManager( xContext->getServiceManager() ); // get an instance of the remote office desktop UNO service // and query the XComponentLoader interface Reference < XComponentLoader > xComponentLoader( xServiceManager->createInstanceWithContext( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.Desktop" ) ), xContext ), UNO_QUERY ); // open a spreadsheet document Reference< XComponent > xComponent( xComponentLoader->loadComponentFromURL( OUString( RTL_CONSTASCII_USTRINGPARAM( "private:factory/scalc" ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_blank" ) ), 0, Sequence < ::com::sun::star::beans::PropertyValue >() ) );
A C++ client application which uses UNO is linked to the C++ UNO libraries,
which can be found in the program directory of a UNO installation. When running
the client application, the C++ UNO libraries are found only, if the UNO
program directory is included in the PATH
(Windows) or
LD_LIBRARY_PATH
(Unix/Linux) environment variable.
Application Loader
As this requires the knowledge of the location of a UNO installation, we
will provide an application loader (unoapploader.exe
for
Windows, unoapploader
for Unix/Linux), which detects
a UNO installation on the system and adds the program directory of the UNO
installation to the PATH
/ LD_LIBRARY_PATH
environment
variable. After that, the application process is loaded and started, whereby the
new process inherits the environment of the calling process, including
the modified PATH
/ LD_LIBRARY_PATH
environment
variable.
For convenience we will provide some tooling in the SDK, which allows to
build a client executable file (e.g. myapplication
for
Unix/Linux), which can be invoked by
./myapplication
In this case, the myapplication
executable is simply the renamed
unoapploader
executable. All the application code is part of a
second executable file, which must have the same name as the first executable,
but prefixed by a underscore '_', that means in the example above the
second executable is named _myapplication
.
On the Unix/Linux platforms the application loader writes error messages to
the stderr
stream. On the Windows platform error messages are
written to the error file <application name>-error.log
in
the application's executable file directory. If this fails, the error file is
written to the directory designated for temporary files.
Note, that the C++ application loader is only available with OOo 2.0.
Finding a UNO installation
A UNO installation can be specified by the user by setting the
UNO_PATH
environment variable to the program directory of a UNO
installation, e.g.
setenv UNO_PATH /opt/OpenOffice.org/program
If no UNO installation is specified by the user, the default installation on the system is taken.
On the Windows platform, the default installation is read from the default value of the key "Software\OpenOffice.org\UNO\InstallPath" from the root key HKEY_CURRENT_USER in the Windows Registry. If this key is missing, the key is read from the root key HKEY_LOCAL_MACHINE.
On the Unix/Linux platforms, the default installation is found from the
PATH
environment variable. This requires that the
soffice
executable or a symbolic link is in one of the directories
listed in the PATH
environment variable.
The bootstrap function
The ::cppu::bootstrap()
function is implemented in a similar
way as the Java com.sun.star.comp.helper.Bootstrap.bootstrap()
method. It first bootstraps a local component context by calling the
::cppu::defaultBootstrap_InitialComponentContext()
function and
then tries to establish a named pipe connection to a running office by using
the com.sun.star.bridge.UnoUrlResolver
service. If the connection
fails, an office process is started. After that, it tries to connect to the
running office again. If the connection succeeds, it gets the remote component
context, which is returned.
Author: Thomas Benisch. Last changed: $Date: 2004/12/05 13:42:29 $.