Language:

The Free and Open Productivity Suite
Apache OpenOffice 4.1.7 released

OpenOfficeHow to write a UNO component in Java


Contents

Introduction
Writing a component
The UNO Base Interface and IDL
Getting a factory for the component
Register the component
Support several services
How to build a component
Deployment hints

Introduction

In this tutorial the implementation of an UNO component in the programming language Java is treated. To the better comprehensibility this tutorial is based on a component for the examination of an object of the OpenOffice API (Instance Inspector). Components like the Instance Inspector should be accessible as beans which you can incorporate into your programs. Actually components implement OpenOffice API services which are an abstract concept providing certain interfaces and properties. You can receive further information for the creation of UNO components in the programming language C++ under the following HTTP address: http://udk.openoffice.org/cpp/man/component_tutorial.html

Writing a component

In general a component can be devided into two main parts and therefore into two classes:

  • The component as a concrete implementation of the service description: in order to provide the functional specification all required interfaces have to be implemented.
  • The factory and the service info: The factory instantiates the component on demand. It must be an implemetation of the interface com.sun.star.lang.XSingleServiceFactory.

The necessary classes could be nested as follows:


public class InstanceInspector {
static private final boolean DEBUG = false; static public class _InstanceInspector implements XInstanceInspector, XInitialization, XTypeProvider { static private final String __serviceName = "org.OpenOffice.InstanceInspector"; private XMultiServiceFactory _xMultiServiceFactory; ... public _InstanceInspector(XMultiServiceFactory xMultiServiceFactory) { _xMultiServiceFactory = xMultiServiceFactory; } public void inspect(java.lang.Object a) throws com.sun.star.uno.RuntimeException { ... } ... } ... }

The class InstanceInspector should provide two methods which are called by the Java Loader. One method (__getServiceFactory()) returns a XSingleServiceFactory for creating the component. The other method (__writeRegistryServiceInfo()) writes the service information into the given registry key.

The local class _InstanceInspector implements three interfaces and is responsible for the concrete implementation of the service description (method inspect()).

The UNO Base Interface and IDL

An interface is a collection of methods that provide a certain functionality. In order to provide a language-independent programming environment, all interfaces must be described in an interface definition language (IDL). As a rule, UNO interfaces are specified in the UNO IDL and must be derived from a mandatory base interface (com.sun.star.uno.XInterface). This base interface provides three methods: queryInterface(), acquire(), and release(). Because UNO supports multiple interfaces, the queryInterface() method is necessary for navigating between the different interfaces (comparable to COM). The acquire() and release() methods handle the life cycle of UNO objects (global reference counting). Cyclic references are handled by the com.sun.star.lang.XComponent interface. Error handling is based on exceptions.

IDL's similarity with the common programming language C++ is remarkable, however IDL contains exclusively syntax for data description and no statements.

The UNO IDL file for the Instance Inspector could look as follows:


#include <com/sun/star/uno/XInterface.idl> 
module org { module OpenOffice { interface XInstanceInspector: com::sun::star::uno::XInterface {
void inspect( [in] any aInstance );
}; }; };

The IDL compiler idlc transforms interface definitions written in IDL into urd files (Uno Reflection Data). The urd files are then going to be merged into the type library (rdb).

For more details on building a component you should take a look at the section How to build a component or the document Mapping of UNO IDL to Java.

Getting a factory for the component

The Service Manager is the central location where components are instantiated. It comes with a list of all available services registered. If a component is requested the Service Manager searches its list for a given service, in order to get the assigned factory. The Java Loader extracts the factory of a component with the aid of a static method called __getServiceFactory. This method should be implemented to return a customized factory for the component. Otherwise the Java Loader will return a default factory.


// Gives a factory for creating the service. 
// This method is called by the JavaLoader 
public static XSingleServiceFactory __getServiceFactory(String implName,
                                                        XMultiServiceFactory multiFactory,
                                                        XRegistryKey regKey) { 
  XSingleServiceFactory xSingleServiceFactory = null;         
  if (implName.equals(_InstanceInspector.class.getName()) ) 
    xSingleServiceFactory = FactoryHelper.getServiceFactory(_InstanceInspector.class,
                                                            _InstanceInspector.__serviceName,
                                                            multiFactory,
                                                            regKey); 
  return xSingleServiceFactory; 
} 

A special class FactoryHelper provides methods for implementing components. This class has default implementations for getting the service factory (method __getServiceFactory) and for writing the service info into the registry (method writeRegistryServiceInfo). Please note, if you implement the service as inner class, then it must be a static inner class. If not, then the constructors of the inner class expect an instance of the outer class as first parameter. However, the FactoryHelper class cannot provide one.

The field __serviceName in your class should specifiy the name of your service.


static private final String __serviceName = "org.OpenOffice.InstanceInspector";

If the property is not specified the default factory sets the components name (class name) as the service name.

Register the component

Every component should be registered with its implementation name for the service. Therefore the method __writeRegistryServiceInfo is responsible for writing the service information into the registry. Registration tools,such as regcomp.exe, need to know what java class contains that method. This information has to be placed into the manifest file. The entry has this format:
RegistrationClassName: TheNameOfTheClass


// Writes the service information into the given registry key.
// This method is called by the JavaLoader
public static boolean __writeRegistryServiceInfo(XRegistryKey regKey)          {
  return FactoryHelper.writeRegistryServiceInfo(_InstanceInspector.class.getName(),
                                                _InstanceInspector.__serviceName,
                                                regKey);
}

Supporting several services

If the component should support more than one service all these services should be specified in the field __serviceName.


static private final String[] __serviceName = { "com.sun.star.X", "com.sun.star.Y" };

Furthermore the component should be able to access the service manager. In order to set the service manager at the component the default factory uses the optional method __setServiceManager.

How to build a component

The following steps describe the build process by the example of the Instance Inspector with the operating system Windows. First you should set the necessary variables. Perhaps you should adapt also the following commands to other operating systems requirements.

The idl compiler idlc transforms the interface definitions written in idl into an urd file:


idlc -I%ODKPATH%\idl .\XInstanceInspector.idl

The urd file is then going to be merged into the type library (rdb):


regmerge .\Types.rdb /UCR .\XInstanceInspector.urd

Regmerge is a small tool to merge different registry files under a specified key into another registry file:


regmerge %OFFICEPROGRAMPATH%\applicat.rdb / .\Types.rdb

The javamaker generates the appropriate java file for each idl type:


javamaker -Torg.OpenOffice.XInstanceInspector -BUCR .\Types.rdb 
                                                  %ODKPATH%\windows\bin\applicat.rdb

Compile the java files:


javac -g -deprecation -classpath %OFFICEPROGRAMPATH%\classes\ridl.jar;
                                 %OFFICEPROGRAMPATH%\classes\jurt.jar;
                                 %OFFICEPROGRAMPATH%\classes\unoil.jar 
                                 .\org\OpenOffice\XInstanceInspector.java 
                                 .\InstanceInspector.java

Use the existing manifest file 'Manifest' and archive all the java files into 'InstanceInspector.jar':


jar -cvfm .\InstanceInspector.jar .\Manifest 
          .\org\OpenOffice\XInstanceInspector.class
          .\InstanceInspector.class 
          .\InstanceInspector$_InstanceInspector.class 
          .\InstanceInspector$1.class 
          .\InstanceInspector$2.class 
          .\InstanceInspector$3.class 
          .\InstanceInspector$4.class

Registering the service, id est the jar-file:


java -classpath %OFFICEPROGRAMPATH%\classes\unoil.jar;
                %OFFICEPROGRAMPATH%\classes\sandbox.jar;
                %OFFICEPROGRAMPATH%\classes\jut.jar;
                %OFFICEPROGRAMPATH%\classes\java_uno.jar;
                %OFFICEPROGRAMPATH%\classes\ridl.jar;
                %OFFICEPROGRAMPATH%\classes\jurt.jar;
                %OFFICEPROGRAMPATH%\classes\juh.jar 
                com.sun.star.tools.uno.RegComp %OFFICEPROGRAMPATH%\applicat.rdb register 
                "file:///e:/Trash/InstanceInspector.jar" com.sun.star.loader.Java2

 

Deployment hints

All interfaces, which are used by the component, have to be known to the Java Virtual Machine. The standard interfaces are contained in the ridl.jar file. That jar is always in the classpath and hence the VM can locate the interface classes.
If you define your own interfaces, then you usually do not want to put them into the ridljar (builds ridl.jar) project from the beginning. Instead you probably like to keep them in the jar file of your component, or in a separate class or jar file during development. If the latter is the case, then you must make sure that the interfaces are in the classpath during registration and when running your component. This can be achieved by editing the SytemClasspath entry (office_dir\user\config\java.ini, or javarc) or the user classpath in the options dialog (tools menu, StarOffice/Security).
Also, do not forget to merge the interface with the applicat.rdb.

Author: Bertram Nolte ( 2002-01-11 10:29 )
Copyright 2002 Sun Microsystems, Inc., 901 San Antonio Road, Palo Alto, CA 94303 USA.



Apache Software Foundation

Copyright & License | Privacy | Contact Us | Donate | Thanks

Apache and the Apache feather logo are trademarks of The Apache Software Foundation. OpenOffice, OpenOffice.org and the seagull logo are registered trademarks of The Apache Software Foundation. Other names appearing on the site may be trademarks of their respective owners.