[ Previous document | Content Table | Next document ]

3  Professional UNO

 

 

This chapter provides in-depth information about UNO and the use of UNO in various programming languages. There are four sections: 

3.1  Introduction

The goal of UNO (Universal Network Objects) is to provide an environment for network objects across programming language and platform boundaries. UNO objects run and communicate everywhere. UNO reaches this goal by providing the following fundamental framework: 

3.2  API Concepts

The OpenOffice.org API is a language independent approach to specify the functionality of OpenOffice.org. Its main goals are to provide an API to access the functionality of OpenOffice.org, to enable users to extend the functionality by their own solutions and new features, and to make the internal implementation of OpenOffice.org exchangeable. 

A long term target on the OpenOffice.org roadmap is to split the existing OpenOffice.org into small components which are combined to provide the complete OpenOffice.org functionality. Such components are manageable, they interact with each other to provide high level features and they are exchangeable with other implementations providing the same functionality, even if these new implementations are implemented in a different programming language. When this target will be reached, the API, the components and the fundamental concepts will provide a construction kit, which makes OpenOffice.org adaptable to a wide range of specialized solutions and not only an office suite with a predefined and static functionality. 

This section provides you with a thorough understanding of the concepts behind the OpenOffice.org API. In the API reference there are UNOIDL data types which are unknown outside of the API. The reference provides abstract specifications which sometimes can make you wonder how they map to implementations you can actually use. 

The data types of the API reference are explained in 3.2.1 Professional UNO - API Concepts - Data Types. The relationship between API specifications and OpenOffice.org implementations is treated in 3.2.2 Professional UNO - API Concepts - Understanding the API Reference.

3.2.1  Data Types

The data types in the API reference are UNO types which have to be mapped to the types of any programming language that can be used with the OpenOffice.org API. In the chapter 2 First Steps the most important UNO types were introduced. However, there is much more to be said about simple types, interfaces, properties and services in UNO. There are special flags, conditions and relationships between these entities which you will want to know if you are working with UNO on a professional level.

This section explains the types of the API reference from the perspective of a developer who wants to use the OpenOffice.org API. If you are interested in writing your own components, and you must define new interfaces and types, please refer to the chapter 4 Writing UNO Components, which describes how to write your own UNOIDL specifications and how to create UNO components.

Simple Types

UNO provides a set of predefined, simple types which are listed in the following table: 

UNO Type 

Description 

void

Empty type, used only as method return type and in any.

boolean

Can be true or false.

byte

Signed 8-bit integer type (ranging from -128 to 127, inclusive). 

short

Signed 16-bit integer type (ranging from −32768 to 32767, inclusive). 

unsigned short

Unsigned 16-bit integer type (deprecated). 

long

Signed 32-bit integer type (ranging from −2147483648 to 2147483647, inclusive). 

unsigned long

Unsigned 32-bit integer type (deprecated). 

hyper

Signed 64-bit integer type (ranging from −9223372036854775808 to 9223372036854775807, inclusive). 

unsigned hyper

Unsigned 64-bit integer type (deprecated). 

float

IEC 60559 single precision floating point type. 

double

IEC 60559 double precision floating point type. 

char

Represents individual Unicode characters (more precisely: individual UTF-16 code units). 

string

Represents Unicode strings (more precisely: strings of Unicode scalar values). 

type

Meta type that describes all UNO types. 

any

Special type that can represent values of all other types. 

The chapters about language bindings 3.4.1 Professional UNO - UNO Language Bindings - Java Language Binding, 3.4.2 Professional UNO - UNO Language Bindings - C++ Language Binding, 3.4.3 Professional UNO - UNO Language Bindings - OpenOffice.org Basic and 3.4.4 Professional UNO - UNO Language Bindings - Automation Bridge describe how these types are mapped to the types of your target language.

The Any Type

The special type any can represent values of all other UNO types. In the target languages, the any type requires special treatment. There is an AnyConverter in Java and special operators in C++. For details, see the section 3.4 Professional UNO - UNO Language Bindings about language bindings.

Interfaces

Communication between UNO objects is based on object interfaces. Interfaces can be seen from the outside or the inside of an object. 

From the outside of an object, an interface provides a functionality or special aspect of the object. Interfaces provide access to objects by publishing a set of operations that cover a certain aspect of an object without telling anything about its internals.

The concept of interfaces is quite natural and frequently used in everyday life. Interfaces allow the creation of things that fit in with each other without knowing internal details about them. A power plug that fits into a standard socket or a one-size-fits-all working glove are simple examples. They all work by standardizing the minimal conditions that must be met to make things work together. 

A more advanced example would be the “remote control aspect” of a simple TV system. One possible feature of a TV system is a remote control. The remote control functions can be described by an XPower and an XChannel interface. The illustration below shows a RemoteControl object with these interfaces:

UML diagram showing the RemoteControl serviceIllustration 3.1: RemoteControl service

The XPower interface has the functions turnOn() and turnOff() to control the power and the XChannel interface has the functions select(), next(),  previous() to control the current channel. The user of these interfaces does not care if he uses an original remote control that came with a TV set or a universal remote control as long as it carries out these functions. The user is only dissatisfied if some of the functions promised by the interface do not work with a remote control.

From the inside of an object, or from the perspective of someone who implements a UNO object, interfaces are abstract specifications. The abstract specification of all the interfaces in the OpenOffice.org API has the advantage that user and implementer can enter into a contract, agreeing to adhere to the interface specification. A program that strictly uses the OpenOffice.org API according to the specification will always work, while an implementer can do whatever he wants with his objects, as long as he serves the contract.

UNO uses the interface type to describe such aspects of UNO objects. By convention, all interface names start with the letter X to distinguish them from other types. All interface types must inherit the com.sun.star.uno.XInterface root interface, either directly or in the inheritance hierarchy. XInterface is explained in 3.3.3 Professional UNO - UNO Concepts - Using UNO Interfaces. The interface types define methods (sometimes also called operations) to provide access to the specified UNO objects.

Interfaces allow access to the data inside an object through dedicated methods (member functions) which encapsulate the data of the object. The methods always have a parameter list and a return value, and they may define exceptions for smart error handling. 

The exception concept in the OpenOffice.org API is comparable with the exception concepts known from Java or C++. All operations can raise com.sun.star.uno.RuntimeExceptions without explicit specification, but all other exceptions must be specified. UNO exceptions are explained in the section 3.3.7 Professional UNO - UNO Concepts - Exception Handling below.

Consider the following two examples for interface definitions in UNOIDL notation. UNOIDL interfaces resemble Java interfaces, and methods look similar to Java method signatures. However, note the flags in square brackets in the following example:

// base interface for all UNO interfaces 

 

interface XInterface 

        any queryInterface( [in] type aType );

        [oneway] void acquire();

        [oneway] void release();

 

};  

 

// fragment of the Interface com.sun.star.io.XInputStream 

 

interface XInputStream: com::sun::star::uno::XInterface 

{  

    long readBytes( [out] sequence<byte> aData,

                    [in] long nBytesToRead )

                raises( com::sun::star::io::NotConnectedException,

                        com::sun::star::io::BufferSizeExceededException,

                        com::sun::star::io::IOException);

    ...

};  

 

The [oneway] flag indicates that an operation can be executed asynchronously if the underlying method invocation system does support this feature. For example, a UNO Remote Protocol (URP) bridge is a system that supports oneway calls.

Pay attention to the following important text section

Although there are no general problems with the specification and the implementation of the UNO oneway feature, there are several API remote usage scenarios where oneway calls cause deadlocks in OpenOffice.org. Therefore, do not introduce new oneway methods with new OpenOffice.org UNO APIs.

There are also parameter flags. Each parameter definition begins with one of the direction flags in, out, or inout to specify the use of the parameter:

These parameter flags do not appear in the API reference. The fact that a parameter is an [out] or [inout] parameter is explained in the method details.

Interfaces consisting of methods form the basis for service specifications. 

Services

We have seen that a single-inheritance interface describes only one aspect of an object. However, it is quite common that objects have more than one aspect. UNO uses multiple-inheritance interfaces and services to specify complete objects which can have many aspects.

In a first step, all the various aspects of an object (which are typically represented by single-inheritance interfaces) are grouped together in one multiple-inheritance interface type. If such an object is obtainable by calling specific factory methods, this step is all that is needed. The factory methods are specified to return values of the given, multiple-inheritance  interface type. If, however, such an object is available as a general service at the global component context, a service description must be provided in a second step. This service description will be of the new style, mapping the service name (under which the service is available at the component context) to the given, multiple-inheritance interface type.

For backward compatibility, there are also old-style services, which comprise a set of single-inheritance interfaces and properties that are needed to support a certain functionality. Such a service can include other old-style services as well. The main drawback of an old-style service is that it is unclear whether it describes objects that can be obtained through specific factory methods (and for which there would therefore be no new-style service description), or whether it describes a general service that is available at the global component context, and for which there would thus be a new-style service description. 

From the perspective of a user of a UNO object, the object offers one or sometimes even several independent, multiple-inheritance interfaces or old-style services described in the API reference. The services are utilized through method calls grouped in interfaces, and through properties, which are handled through special interfaces as well. Because the access to the functionality is provided by interfaces only, the implementation is irrelevant to a user who wants to use an object.

From the perspective of an implementer of a UNO object, multiple-inheritance interfaces and old-style services are used to define a functionality independently of a programming language and without giving instructions about the internal implementation of the object. Implementing an object means that it must support all specified interfaces and properties. It is possible that a UNO object implements more than one independent, multiple-inheritance interface or old-style service. Sometimes it is useful to implement two or more independent, multiple-inheritance interfaces or services because they have related functionality, or because they support different views to the object.

Illustration 3.2 shows the relationship between interfaces and services. The language independent specification of an old-style service with several interfaces is used to implement a UNO object that fulfills the specification. Such a UNO object is sometimes called a “component,” although that term is more correctly used to describe deployment entities within a UNO environment. The illustration uses an old-style service description that directly supports multiple interfaces; for a new-style service description, the only difference would be that it would only support one multiple-inheritance interface, which in turn would inherit the other interfaces.

Service specification against service implementationIllustration 3.2: Interfaces, services and implementation

The functionality of a TV system with a TV set and a remote control can be described in terms of service specifications. The interfaces XPower and XChannel described above would be part of a service specification RemoteControl. The new service TVSet consists of the three interfaces XPower, XChannel and XStandby to control the power, the channel selection, the additional power function standby() and a timer() function.

UML diagram showing a TVSet serviceIllustration 3.3: TV System Specification
Referencing Interfaces

References to interfaces in a service definition mean that an implementation of this service must offer the specified interfaces. However, optional interfaces are possible. If a multiple-inheritance interface inherits an optional interface, or an old-style service contains an optional interface, any given UNO object may or may not support this interface. If you utilize an optional interface of a UNO object, always check if the result of queryInterface() is equal to null and react accordingly—otherwise your code will not be compatible with implementations without the optional interface and you might end up with null pointer exceptions. The following UNOIDL snippet shows a fragment of the specification for the old-style com.sun.star.text.TextDocument service in the OpenOffice.org API. Note the flag optional in square brackets, which makes the interfaces XFootnotesSupplier and XEndnotesSupplier non-mandatory.

// com.sun.star.text.TextDocument 

service TextDocument 

    ...

 

    interface com::sun::star::text::XTextDocument;

    interface com::sun::star::util::XSearchable;

    interface com::sun::star::util::XRefreshable;

    [optional] interface com::sun::star::text::XFootnotesSupplier;

    [optional] interface com::sun::star::text::XEndnotesSupplier;

 

    ...

}; 

Service Constructors

New-style services can have constructors, similar to interface methods: 

service SomeService: XSomeInterface {
   create1();

    create2([in] long arg1, [in] string arg2);

    create3([in] any... rest);
};

In the above example, there are three explicit constructors, named create1, create2, and create3. The first has no parameters, the second has two normal parameters, and the third has a special rest parameter, which accepts an arbitrary number of any values. Constructor parameters may only be [in], and a rest parameter must be the only parameter of a constructor, and must be of type any; also, unlike an interface method, a service constructor does not specify a return type.

The various language bindings map the UNO constructors into language-specific constructs, which can be used in client code to obtain instances of those services, given a component context. The general convention (followed,for example, by the Java and C++ language bindings) is to map each constructor to a static method (resp. function) with the same name, that takes as a first parameter an XComponentContext, followed by all the parameters specified in the constructor, and returns an (appropriately typed) service instance. If an instance cannot be obtained, a com.sun.star.uno.DeploymentException is thrown. The above SomeService would map to the following Java 1.5 class, for example:

public class SomeService {
   public static XSomeInterface create1(

        com.sun.star.uno.XComponentContext context) { ... }

    public static XSomeInterface create2(

        com.sun.star.uno.XComponentContext context, int arg1, String arg2) { ... }

    public static XSomeInterface create3(

        com.sun.star.uno.XComponentContext context, Object... rest) { ... }
}

Service constructors can also have exception specifications (“raises (Exception1, ...)”), which are treated in the same way as exception specifications of interface methods. (If a constructor has no exception specification, it may only throw runtime exceptions, com.sun.star.uno.DeploymentException in particular.)

If a new-style service is written using the short form, 

service SomeService: XSomeInterface; 

then it has an implicit constructor. The exact behavior of the implicit constructor is language-binding–specific, but it is typically named create, takes no arguments besides the XComponentContext, and may only throw runtime exceptions.

Including Properties

When the structure of the OpenOffice.org API was founded, the designers discovered that the objects in an office environment would have huge numbers of qualities that did not appear to be part of the structure of the objects, rather they seemed to be superficial changes to the underlying objects. It was also clear that not all qualities would be present in each object of a certain kind. Therefore, instead of defining a complicated pedigree of optional and non-optional interfaces for each and every quality, the concept of properties was introduced. Properties are data in an object that are provided by name over a generic interface for property access, that contains getPropertyValue() and setPropertyValue() access methods. The concept of properties has other advantages, and there is more to know about properties. Please refer to 3.3.4 Professional UNO - UNO Concepts - Properties for further information about properties.

Old-style services can list supported properties directly in the UNOIDL specification. A property defines a member variable with a specific type that is accessible at the implementing component by a specific name. It is possible to add further restrictions to a property through additional flags. The following old-style service references one interface and three optional properties. All known API types can be valid property types:

// com.sun.star.text.TextContent 

service TextContent 

    interface com::sun::star::text::XTextContent;

    [optional, property] com::sun::star::text::TextContentAnchorType AnchorType;

    [optional, readonly, property] sequence<com::sun::star::text::TextContentAnchorType> AnchorTypes;

    [optional, property] com::sun::star::text::WrapTextMode TextWrap;

}; 

Possible property flags are: 

Referencing other Services

Old-style services can include other old-style services. Such references may be optional. That a service is included by another service has nothing to do with implementation inheritance, only the specifications are combined. It is up to the implementer if he inherits or delegates the necessary functionality, or if he implements it from scratch.  

The old-style service com.sun.star.text.Paragraph in the following UNOIDL example includes one mandatory service com.sun.star.text.TextContent and five optional services. Every Paragraph must be a TextContent. It can be a TextTable and it is used to support formatting properties for paragraphs and characters:

// com.sun.star.text.Paragraph 

service Paragraph 

    service com::sun::star::text::TextContent;

    [optional] service com::sun::star::text::TextTable;

    [optional] service com::sun::star::style::ParagraphProperties;

    [optional] service com::sun::star::style::CharacterProperties;

    [optional] service com::sun::star::style::CharacterPropertiesAsian;

    [optional] service com::sun::star::style::CharacterPropertiesComplex;

 

    ...

}; 

If all the old-style services in the example above were multiple-inheritance interface types instead, the structure would be similar: the multiple-inheritance interface type Paragraph would inherit the mandatory interface TextContent and the optional interfaces TextTable, ParagraphProperties, etc.

Service Implementations in Components

A component is a shared library or Java archive containing implementations of one or more services in one of the target programming languages supported by UNO. Such a component must meet basic requirements, mostly different for the different target language, and it must support the specification of the implemented services. That means all specified interfaces and properties must be implemented. Components must be registered in the UNO runtime system. After the registration all implemented services can be used by ordering an instance of the service at the appropriate service factory and accessing the functionality over interfaces.

Based on our example specifications for a TVSet and a RemoteControl service, a component RemoteTVImpl could simulate a remote TV system:

UML diagram showing  a RmoteTV componentIllustration 3.4: RemoteTVImpl Component

Such a RemoteTV component could be a jar file or a shared library. It would contain two service implementations, TVSet and RemoteControl. Once the RemoteTV component  is registered with the global service manager, users can call the factory method of the service manager and ask for a TVSet or a RemoteControl service. Then they could use their functionality over the interfaces XPower, XChannel and XStandby. When a new  implementation of these services with better performance or new features is available later on, the old component can be replaced without breaking existing code, provided that the new features are introduced by adding interfaces.

Structs

A struct type defines several elements in a record. The elements of a struct are UNO types with a unique name within the struct. Structs have the disadvantage not to encapsulate data, but the absence of get() and set() methods can help to avoid the overhead of method calls over a UNO bridge. UNO supports single inheritance for struct types. A derived struct recursively inherits all elements of the parent and its parents.

// com.sun.star.lang.EventObject 

/** specifies the base for all event objects and identifies the 

        source of the event.

 */

struct EventObject 

        /** refers to the object that fired the event.

        */

        com::sun::star::uno::XInterface Source;

 

};  

 

// com.sun.star.beans.PropertyChangeEvent 

struct PropertyChangeEvent : com::sun::star::lang::EventObject { 

    string PropertyName;

    boolean Further;

    long PropertyHandle;

    any OldValue;

    any NewValue;

}; 

A new feature of OpenOffice.org 2.0 is the polymorphic struct type. A polymorphic struct type template is similar to a plain struct type, but it has one or more type parameters, and its members can have these parameters as types. A polymorphic struct type template is not itself a UNO type—it has to be instantiated with actual type arguments to be used as a type.

// A polymorphic struct type template with two type parameters:
struct Poly<T,U> {

    T member1;

    T member2;

    U member3;

    long member4;

}; 

 

// Using an instantiation of Poly as a UNO type:
interface XIfc { Poly<boolean, any> fn(); };

In the example, Poly<boolean, any> will be an instantiated polymorphic struct type with the same form as the plain struct type

struct PolyBooleanAny {
   boolean member1;

    boolean member2;

    any member3;

    long member4;
};

Polymorphic struct types were added primarily to support rich interface type attributes that are as expressive as maybeambiguous, maybedefault, or maybevoid properties (see com.sun.star.beans.Ambiguous, com.sun.star.beans.Defaulted, com.sun.star.beans.Optional), but they are probably useful in other contexts, too.

Predefined Values

The API offers many predefined values, that are used as method parameters, or returned by methods. In UNO IDL there are two different data types for predefined values: constants and enumerations. 

const

A const defines a named value of a valid UNO IDL type. The value depends on the specified type and can be a literal (integer number, floating point number or a character), an identifier of another const type or an arithmetic term using the operators: +, -, *, /, ~, &, |, %, ^, <<, >>.

Since a wide selection of types and values is possible in a const, const is occasionally used to build bit vectors which encode combined values. 

const short ID = 23; 

const boolean ERROR = true; 

const double PI = 3.1415; 

Usually const definitions are part of a constants group. 

constants

The constants type defines a named group of const values. A const in a constants group is denoted by the group name and the const name. In the UNO IDL example below, ImageAlign.RIGHT refers to the value 2:

constants ImageAlign { 

    const short LEFT = 0;

    const short TOP = 1;

    const short RIGHT = 2;

    const short BOTTOM = 3;

}; 

enum

An enum type is equivalent to an enumeration type in C++. It contains an ordered list of one or more identifiers representing long values of the enum type. By default, the values are numbered sequentially, beginning with 0 and adding 1 for each new value. If an enum value has been assigned a value, all following enum values without a predefined value get a value starting from this assigned value.

// com.sun.star.uno.TypeClass 

enum TypeClass { 

    VOID,

    CHAR,

    BOOLEAN,

    BYTE,

    SHORT,

    ...

}; 

 

enum Error { 

    SYSTEM = 10, // value 10

    RUNTIME,     // value 11

    FATAL,       // value 12

    USER = 30,   // value 30

    SOFT         // value 31

}; 

If enums are used during debugging, you should be able to derive the numeric value of an enum by counting its position in the API reference. However, never use literal numeric values instead of enums in your programs. 

Pay attention to the following important text section

Once an enum type has been specified and published, you can trust that it is not extended later on, for that would break existing code. However, new const vaues may be added to a constant group. 

Sequences

A sequence type is a set of elements of the same type, that has a variable number of elements. In UNO IDL, the used element always references an existing and known type or another sequence type. A sequence can occur as a normal type in all other type definitions.

sequence< com::sun::star::uno::XInterface > 

sequence< string > getNamesOfIndex( sequence< long > indexes ); 

Modules

Modules are namespaces, similar to namespaces in C++ or packages in Java. They group services, interfaces, structs, exceptions, enums, typedefs, constant groups and submodules with related functional content or behavior. They are utilized to specify coherent blocks in the API, this allows for a well-structured API. For example, the module com.sun.star.text contains interfaces and other types for text handling. Some other typical modules are com.sun.star.uno, com.sun.star.drawing, com.sun.star.sheet and com.sun.star.table. Identifiers inside a module do not clash with identifiers in other modules, therefore it is possible for the same name to occur more than once. The global index of the API reference shows that this does happen.

Although it may seem that the modules correspond with the various parts of OpenOffice.org, there is no direct relationship between the API modules and the OpenOffice.org applications Writer, Calc and Draw. Interfaces from the module com.sun.star.text are used in Calc and Draw. Modules like com.sun.star.style or com.sun.star.document provide generic services and interfaces that are not specific to any one part of OpenOffice.org.

The modules you see in the API reference were defined by nesting UNO IDL types in module instructions. For example, the module com.sun.star.uno contains the interface XInterface:

module com { 

    module sun {

        module star {

            module uno {

                interface XInterface {

                    ...

                };

            };

        };

    };

}; 

Exceptions

An exception type indicates an error to the caller of a function. The type of an exception gives a basic description of the kind of error that occurred. In addition, the UNO IDL exception types contain elements which allow for an exact specification and a detailed description of the error. The exception type supports inheritance, this is freqzuently used to define a hierarchy of errors. Exceptions are only used to raise errors, not  as method parameters or return types.

UNO IDL requires that all exceptions must inherit from com.sun.star.uno.Exception. This is a precondition for the UNO runtime.

// com.sun.star.uno.Exception is the base exception for all exceptions 

exception Exception { 

    string Message;

    Xinterface Context;

}; 

 

// com.sun.star.uno.RuntimeException is the base exception for serious problems 

// occuring at runtime, usually programming errors or problems in the runtime environment 

exception RuntimeException : com::sun::star::uno::Exception { 

}; 

 

// com.sun.star.uno.SecurityException is a more specific RuntimeException  

exception SecurityException : com::sun::star::uno::RuntimeException { 

}; 

Exceptions may only be thrown by operations which were specified to do so. In contrast, com.sun.star.uno.RuntimeExceptions can always occur.

Pay attention to the following important text section

The methods acquire() and release of the UNO base interface com.sun.star.uno.XInterface are an exception to the above rule. They are the only operations that may not even throw runtime exceptions. But in Java and C++ programs, you do not use these methods directly, they are handled by the respective language binding.

Singletons

Singletons are used to specify named objects where exactly one instance can exist in the life of a UNO component context. A singleton references one interface type and specifies that the only existing instance of this singleton can be reached over the component context using the name of the singleton. If no instance of the singleton exists, the component context will instantiate a new one. An example of such a new-style singleton is

module com { module sun { module star { module deployment {
singleton thePackageManagerFactory: XPackageManagerFactory;
}; }; }; };

The various language bindings offer language-specific ways to obtain the instance of a new-style singleton, given a component context. For example, in Java and C++ there is a static method (resp. function) named get, that takes as its only argument an XComponentContext and returns the (appropriately typed) singleton instance. If the instance cannot be obtained, a com.sun.star.uno.DeploymentException is thrown.

There are also old-style singletons, which reference (old-style) services instead of interfaces. However, for old-style services, the language bindings offer no get functionality.

3.2.2  Understanding the API Reference

Specification, Implementation and Instances

The API specifications you find in the API reference are abstract. The service descriptions of the API reference are not about classes that previously exist somewhere. The specifications are first, then the UNO implementation is created according to the specification. That holds true even for legacy implementations that had to be adapted to UNO. 

Moreover, since a component developer is free to implement services and interfaces as required, there is not necessarily a one-to-one relationship between a certain service specification and a real object. The real object can be capable of more things than specified in a service definition. For example, if you order a service at the factory or receive an object from a getter or getPropertyValue() method, the specified features will be present, but there may be additional features. For instance, the text document model has a few interfaces which are not included in the specification for the com.sun.star.text.TextDocument.

Because of the optional interfaces and properties, it is impossible to comprehend fully from the API reference what a given instance of an object in OpenOffice.org is capable of. The optional interfaces and properties are correct for an abstract specification, but it means that when you leave the scope of mandatory interfaces and properties, the reference only defines how things are allowed to work, not how they actually work.  

Another important point is the fact that there are several entry points where object implementations are actually available. You cannot instantiate every old-style service that can be found in the API reference by means of the global service manager. The reasons are: 

In the first and the last case above, using multiple-inheritance interface types instead of old-style services would have been the right design choice, but the mentioned services predate the availability of multiple-inheritance interface types in UNO. 

Consequently, it is sometimes confusing to look up a needed functionality in the API reference, for you need a basic understanding how a functionality works, which services are involved, where they are available etc., before you can really utilize the reference. This manual aims at giving you this understanding about the OpenOffice.org document models, the database integration and the application itself. 

Object Composition

Interfaces support single and multiple inheritance, and they are all based on com.sun.star.uno.XInterface. In the API reference, this is mirrored in the Base Hierarchy section of any interface specification. If you look up an interface, always check the base hierarchy section to understand the full range of supported methods. For instance, if you look up com.sun.star.text.XText, you see two methods, insertTextContent() and removeTextContent(), but  there are nine more methods provided by the inherited interfaces. The same applies to exceptions and sometimes also to structs, which support single inheritance as well.

The service specifications in the API reference can contain a section  Included Services , which is similar to the above in that a single included old-style service might encompass a whole world of services. However, the fact that a service is included has nothing to do with class inheritance. In which manner a service implementation technically includes other services, by inheriting from base implementations, by aggregation, some other kind of delegation or simply by re-implementing everything is by no means defined (which it is not, either, for UNO interface inheritance). And it is uninteresting for an API user – he can absolutely rely on the availability of the described functionality, but he must never rely on inner details of the implementation, which classes provide the functionality, where they inherit from and what they delegate to other classes.

3.3  UNO Concepts

Now that you have an advanced understanding of OpenOffice.org API concepts and you understand the specification of UNO objects, we are ready to explore UNO, i.e. to see how UNO objects connect and communicate with each other. 

3.3.1  UNO Interprocess Connections

UNO objects in different environments connect via the interprocess bridge. You can execute calls on UNO object instances, that are located in a different process. This is done by converting the method name and the arguments into a byte stream representation, and sending this package to the remote process, for example, through a socket connection. Most of the examples in this manual use the interprocess bridge to communicate with the OpenOffice.org.      

This section deals with the creation of UNO interprocess connections using the UNO API. 

Starting OpenOffice.org in Listening Mode

Most examples in this developers guide connect to a running OpenOffice.org and perform API calls, which are then executed in OpenOffice.org. By default, the office does not listen on a resource for security reasons. This makes it necessary to make OpenOffice.org listen on an interprocess connection resource, for example, a socket. Currently this can be done in two ways: 

Choose the procedure that suits your requirements and launch OpenOffice.org in listening mode now. Check if it is listening by calling netstat -a or -na on the command-line. An output similar to the following shows that the office is listening:

TCP    <Hostname>:8100   <Fully qualified hostname>: 0 Listening

If you use the -n option, netstat displays addresses and port numbers in numerical form. This is sometimes useful on UNIX systems where it is possible to assign logical names to ports.

If the office is not listening, it probably was not started with the proper connection URL parameter. Check the Setup.xcu file or your command-line for typing errors and try again.

Note graphics marks a special text section

Note: In versions before OpenOffice.org 1.1.0, there are several differences.  

The configuration setting that makes the office listen everytime is located elsewhere. Open the file <OfficePath>/share/config/registry/instance/org/openoffice/Setup.xml in an editor, and look for the element:

<ooSetupConnectionURL cfg:type="string"/>  

Extend it with the following code: 

<ooSetupConnectionURL cfg:type="string">
socket,port=2083;urp;
</ooSetupConnectionURL>

The commandline option -accept is ignored when there is a running instance of the office, including the quick starter and the online help. If you use it, make sure that no soffice process runs on your system.

The various parts of the connection URL will be discussed in the next section. 

Importing a UNO Object

The most common use case of interprocess connections is to import a reference to a UNO object from an exporting server. For instance, most of the Java examples described in this manual retrieve a reference to the OpenOffice.org ComponentContext. The correct way to do this is using the com.sun.star.bridge.UnoUrlResolver service. Its main interface com.sun.star.bridge.XUnoUrlResolver is defined in the following way:

interface XUnoUrlResolver: com::sun::star::uno::XInterface 

{
   /** resolves an object on the UNO URL */
   com::sun::star::uno::XInterface resolve( [in] string sUnoUrl )
       raises (com::sun::star::connection::NoConnectException,
               com::sun::star::connection::ConnectionSetupException,
               com::sun::star::lang::IllegalArgumentException);
};

The string passed to the resolve() method is called a UNO URL. It must have the following format:

Graphic showing an UNO Url scheme

An example URL could be uno:socket,host=localhost,port=2002;urp;StarOffice.ServiceManager. The parts of this URL are:

  1. The URL schema uno:. This identifies the URL as UNO URL and distinguishes it from others, such as http: or ftp: URLs.

  2. A string which characterizes the type of connection to be used to access the other process. Optionally, directly after this string, a comma separated list of name-value pairs can follow, where name and value are separated by a '='. The currently supported connection types are described in 3.3.1 Professional UNO - UNO Concepts - UNO Interprocess Connections - Opening a Connection. The connection type specifies the transport mechanism used to transfer a byte stream, for example, TCP/IP sockets or named pipes.

  3. A string which characterizes the type of protocol used to communicate over the established byte stream connection. The string can be followed by a comma separated list of name-value pairs, which can be used to customize the protocol to specific needs. The suggested protocol is urp (UNO Remote Protocol). Some useful parameters are explained below. Refer to the document named UNO-URL at udk.openoffice.org. for the complete specification.

  4. A process must explicitly export a certain object by a distinct name. It is not possible to access an arbitrary UNO object (which would be possible with IOR in CORBA, for instance).

The following example demonstrates how to import an object using the UnoUrlResolver: (ProfUNO/InterprocessConn/UrlResolver.java):

    XComponentContext xLocalContext =

        com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);

       

    // initial serviceManager

    XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();

               

    // create a URL resolver

    Object urlResolver = xLocalServiceManager.createInstanceWithContext(

        "com.sun.star.bridge.UnoUrlResolver", xLocalContext);

 

    // query for the XUnoUrlResolver interface

    XUnoUrlResolver xUrlResolver =

        (XUnoUrlResolver) UnoRuntime.queryInterface(XUnoUrlResolver.class, urlResolver);

 

    // Import the object

    Object rInitialObject = xUrlResolver.resolve(

        “uno:socket,host=localhost,port=2002;urp;StarOffice.ServiceManager”);

           

    // XComponentContext

    if (null != rInitialObject) {

        System.out.println("initial object successfully retrieved");

    } else {

        System.out.println("given initial-object name unknown at server side");

    }

The usage of the UnoUrlResolver has certain disadvantages. You cannot:

These issues are addressed by the underlying API, which is explained below. in 3.3.1 Professional UNO - UNO Concepts - UNO Interprocess Connections - Opening a Connection.

Characteristics of the Interprocess Bridge

The whole bridge is threadsafe and allows multiple threads to execute remote calls. The dispatcher thread inside the bridge cannot block because it never executes calls. It instead passes the requests to worker threads.

 

Pay attention to the following important text section

Although there are no general problems with the specification and the implementation of the UNO oneway feature, there are several API remote usage scenarios where oneway calls cause deadlocks in OpenOffice.org. Therefore do not introduce new oneway methods with new OpenOffice.org UNO APIs.

For synchronous requests, thread identity is guaranteed. When process A calls process B, and process B calls process A, the same thread waiting in process A will take over the new request. This avoids deadlocks when the same mutex is locked again. For asynchronous requests, this is not possible because there is no thread waiting in process A. Such requests are executed in a new thread. The series of calls between two processes is guaranteed. If two asynchronous requests from process A are sent to process B, the second request waits until the first request is finished.

Although the remote bridge supports asynchronous calls, this feature is disabled by default. Every call is executed synchronously. The oneway flag of UNO interface methods is ignored. However, the bridge can be started in a mode that enables the oneway feature and thus executes calls flagged with the [oneway] modifier as  asynchronous calls. To do this, the protocol part of the connection string on both sides of the remote bridge must be extended by ',Negotiate=0,ForceSynchronous=0' . For example:

soffice “-accept=socket,host=0,port=2002;urp,Negotiate=0,ForceSynchronous=0;”

for starting the office and  

"uno:socket,host=localhost,port=2002;urp,Negotiate=0,ForceSynchronous=0;StarOffice.ServiceManager"

as UNO URL for connecting to it.  

Pay attention to the following important text section

The asynchronous mode can cause deadlocks in OpenOffice.org. It is recommended not to activate it if one side of the remote bridge is OpenOffice.org. 

Opening a Connection

The method to import a UNO object using the UnoUrlResolver has drawbacks as described in the previous chapter. The layer below the UnoUrlResolver offers full flexibility in interprocess connection handling.

UNO interprocess bridges are established on the com.sun.star.connection.XConnection interface, which encapsulates a reliable bidirectional byte stream connection (such as a TCP/IP connection).

interface XConnection: com::sun::star::uno::XInterface 

{  

    long read( [out] sequence < byte > aReadBytes , [in] long nBytesToRead )

                raises( com::sun::star::io::IOException );

    void write( [in] sequence < byte > aData )

                raises( com::sun::star::io::IOException );

    void flush( ) raises( com::sun::star::io::IOException );

    void close( ) raises( com::sun::star::io::IOException );

    string getDescription();

}; 

There are different mechanisms to establish an interprocess connection. Most of these mechanisms follow a similar pattern. One process listens on a resource and waits for one or more processes to connect to this resource. 

This pattern has been abstracted by the services com.sun.star.connection.Acceptor that exports the com.sun.star.connection.XAcceptor interface and com.sun.star.connection.Connector that exports the com.sun.star.connection.XConnector interface.

interface XAcceptor: com::sun::star::uno::XInterface 

{  

    XConnection accept( [in] string sConnectionDescription )

                raises( AlreadyAcceptingException,

                ConnectionSetupException,

                com::sun::star::lang::IllegalArgumentException);

 

    void stopAccepting();

};  

 

interface XConnector: com::sun::star::uno::XInterface 

{  

    XConnection connect( [in] string sConnectionDescription )

                raises( NoConnectException,ConnectionSetupException );

};  

The acceptor service is used in the listening process while the connector service is used in the actively connecting service. The methods accept() and connect() get the connection string as a parameter. This is the connection part of the UNO URL (between uno: and ;urp).

The connection string consists of a connection type followed by a comma separated list of name-value pairs. The following table shows the connection types that are supported by default. 

Connection type 

 

socket 

Reliable TCP/IP socket connection 

Parameter 

Description 

host 

Hostname or IP number of the resource to listen on/connect. May be localhost. In an acceptor string, this may be 0 ('host=0'), which means, that it accepts on all available network interfaces.

port 

TCP/IP port number to listen on/connect to. 

tcpNoDelay 

Corresponds to the socket option tcpNoDelay. For a UNO connection, this parameter should be set to 1 (this is NOT the default it must be added explicitly). If the default is used (0), it may come to 200 ms delays at certain call combinations.

pipe 

A named pipe (uses shared memory). This type of interprocess connection is marginally faster than socket connections and works only if both processes are located on the same machine. It does not work on Java by default, because Java does not support named pipes directly 

Parameter 

Description 

name 

Name of the named pipe. Can only accept one process on name on one machine at a time. 

Tip graphics marks a hint section in the text

You can add more kinds of interprocess connections by implementing connector and acceptor services, and choosing the service name by the scheme com.sun.star.connection.Connector.<connection-type>, where <connection-type> is the name of the new connection type.

If you implemented the service com.sun.star.connection.Connector.mytype, use the UnoUrlResolver with the URL 'uno:mytype,param1=foo;urp;StarOffice.ServiceManager' to establish the interprocess connection to the office.

Creating the Bridge

UML diagram showing the initiaton of a brdigeIllustration 3.5: The interaction of services that are needed to initiate a UNO interprocess bridge. The interfaces have been simplified.

The XConnection instance can now be used to establish a UNO interprocess bridge on top of the connection, regardless if the connection was established with a Connector or Acceptor service (or another method). To do this, you must instantiate the service com.sun.star.bridge.BridgeFactory. It supports the com.sun.star.bridge.XBridgeFactory interface.

interface XBridgeFactory: com::sun::star::uno::XInterface 

{  

    XBridge createBridge(

            [in] string sName,

            [in] string sProtocol ,

            [in] com::sun::star::connection::XConnection aConnection ,

            [in] XInstanceProvider anInstanceProvider )

        raises ( BridgeExistsException , com::sun::star::lang::IllegalArgumentException );

    XBridge getBridge( [in] string  sName );

    sequence < XBridge > getExistingBridges( );

};  

The BridgeFactory service administrates all UNO interprocess connections. The createBridge() method creates a new bridge:

interface XInstanceProvider: com::sun::star::uno::XInterface 

{  

    com::sun::star::uno::XInterface getInstance( [in] string sInstanceName )

                raises ( com::sun::star::container::NoSuchElementException );

};  

The BridgeFactory returns a com.sun.star.bridge.XBridge interface.

interface XBridge: com::sun::star::uno::XInterface 

{  

    XInterface getInstance( [in] string sInstanceName );

    string getName();

    string getDescription();

};  

The XBridge.getInstance() method retrieves an initial object from the remote counterpart. The local XBridge.getInstance() call arrives in the remote process as an XInstanceProvider.getInstance() call. The object returned can be controlled by the string sInstanceName. It completely depends on the implementation of XInstanceProvider, which object it returns.

The XBridge interface can be queried for a com.sun.star.lang.XComponent interface, that adds a com.sun.star.lang.XEventListener to the bridge. This listener will be terminated when the underlying connection closes (see above). You can also call dispose() on the XComponent interface explicitly, which closes the underlying connection and initiates the bridge shutdown procedure.

Closing a Connection

The closure of an interprocess connection can occur for the following reasons: 

Except for the first reason, all other connection closures initiate an interprocess bridge shutdown procedure. All pending synchronous requests abort with a com.sun.star.lang.DisposedException, which is derived from the com.sun.star.uno.RuntimeException. Every call that is initiated on a disposed proxy throws a DisposedException. After all threads have left the bridge (there may be a synchronous call from the former remote counterpart in the process), the bridge explicitly releases all stubs to the original objects in the local process, which were previously held by the former remote counterpart. The bridge then notifies all registered listeners about the disposed state using com.sun.star.lang.XEventListener. The example code for a connection-aware client below shows how to use this mechanism. The bridge itself is destroyed, after the last proxy has been released.

Unfortunately, the various listed error conditions are not distinguishable. 

Example: A Connection Aware Client

The following example shows an advanced client which can be informed about the status of the remote bridge. A complete example for a simple client is given in the chapter 2 First Steps.

The following Java example opens a small awt window containing the buttons new writer and new calc that opens a new document and a status label. It connects to a running office when a button is clicked for the first time. Therefore it uses the connector/bridge factory combination, and registers itself as an event listener at the interprocess bridge.

When the office is terminated, the disposing event is terminated, and the Java program sets the text in the status label to 'disconnected' and clears the office desktop reference. The next time a button is pressed, the program knows that it has to re-establish the connection. 

The method getComponentLoader() retrieves the XComponentLoader reference on demand:

(ProfUNO/InterprocessConn/ConnectionAwareClient.java

    XComponentLoader _officeComponentLoader = null;

 

    // local component context

    XComponentContext _ctx;    

 

    protected com.sun.star.frame.XComponentLoader getComponentLoader()

            throws com.sun.star.uno.Exception {

        XComponentLoader officeComponentLoader = _officeComponentLoader;

 

        if (officeComponentLoader == null) {

            // instantiate connector service

            Object x = _ctx.getServiceManager().createInstanceWithContext(

                "com.sun.star.connection.Connector", _ctx);

           

            XConnector xConnector = (XConnector) UnoRuntime.queryInterface(XConnector.class, x);

 

            // helper function to parse the UNO URL into a string array

            String a[] = parseUnoUrl(_url);

            if (null == a) {

                throw new com.sun.star.uno.Exception("Couldn't parse UNO URL "+ _url);

            }

 

            // connect using the connection string part of the UNO URL only.

            XConnection connection = xConnector.connect(a[0]);

       

            x = _ctx.getServiceManager().createInstanceWithContext(

                "com.sun.star.bridge.BridgeFactory", _ctx);

 

            XBridgeFactory xBridgeFactory = (XBridgeFactory) UnoRuntime.queryInterface(

                XBridgeFactory.class , x);

 

            // create a nameless bridge with no instance provider

            // using the middle part of the UNO URL

            XBridge bridge = xBridgeFactory.createBridge("" , a[1] , connection , null);

 

            // query for the XComponent interface and add this as event listener

            XComponent xComponent = (XComponent) UnoRuntime.queryInterface(

                XComponent.class, bridge);

            xComponent.addEventListener(this);

 

            // get the remote instance

            x = bridge.getInstance(a[2]);

 

            // Did the remote server export this object ?

            if (null == x) {

                throw new com.sun.star.uno.Exception(

                    "Server didn't provide an instance for" + a[2], null);

            }

     

            // Query the initial object for its main factory interface

            XMultiComponentFactory xOfficeMultiComponentFactory = (XMultiComponentFactory)

                UnoRuntime.queryInterface(XMultiComponentFactory.class, x);

 

            // retrieve the component context (it's not yet exported from the office)

            // Query for the XPropertySet interface.

            XPropertySet xProperySet = (XPropertySet)

                UnoRuntime.queryInterface(XPropertySet.class, xOfficeMultiComponentFactory);

           

            // Get the default context from the office server.

            Object oDefaultContext =

                xProperySet.getPropertyValue("DefaultContext");

           

            // Query for the interface XComponentContext.

            XComponentContext xOfficeComponentContext =

                (XComponentContext) UnoRuntime.queryInterface(

                    XComponentContext.class, oDefaultContext);

 

           

            // now create the desktop service

            // NOTE: use the office component context here !

            Object oDesktop = xOfficeMultiComponentFactory.createInstanceWithContext(

                "com.sun.star.frame.Desktop", xOfficeComponentContext);

 

            officeComponentLoader = (XComponentLoader)

                UnoRuntime.queryInterface( XComponentLoader.class, oDesktop);

 

            if (officeComponentLoader == null) {

                throw new com.sun.star.uno.Exception(

                    "Couldn't instantiate com.sun.star.frame.Desktop" , null);

            }

            _officeComponentLoader = officeComponentLoader;

        }

        return officeComponentLoader;

    }

This is the button event handler: 

    public void actionPerformed(ActionEvent event) {

        try {

            String sUrl;

            if (event.getSource() == _btnWriter) {

                sUrl = "private:factory/swriter";

            } else {

                sUrl = "private:factory/scalc";

            }

            getComponentLoader().loadComponentFromURL(

                sUrl, "_blank", 0,new com.sun.star.beans.PropertyValue[0]);

            _txtLabel.setText("connected");

        } catch (com.sun.star.connection.NoConnectException exc) {

            _txtLabel.setText(exc.getMessage());

        } catch (com.sun.star.uno.Exception exc) {

            _txtLabel.setText(exc.getMessage());

            exc.printStackTrace();

            throw new java.lang.RuntimeException(exc.getMessage());

        }

    }

And the disposing handler clears the _officeComponentLoader reference:

    public void disposing(com.sun.star.lang.EventObject event) {

        // remote bridge has gone down, because the office crashed or was terminated.

        _officeComponentLoader = null;

        _txtLabel.setText("disconnected");

    }

3.3.2  Service Manager and Component Context

This chapter discusses the root object for connections to OpenOffice.org (and to any UNO application) – the service manager. The root object serves as the entry point for every UNO application and is passed to every UNO component during instantiation.

Two different concepts to get the root object currently exist. StarOffice6.0 and OpenOffice.org1.0 use the previous concept. Newer versions or product patches use the newer concept and provide the previous concept for compatibility issues only. First we will look at the previous concept, the service manager as it is used in the main parts of the underlying OpenOffice.org implementation of this guide. Second, we will introduce the component context—which is the newer concept and explain the migration path.

Service Manager

The com.sun.star.lang.ServiceManager is the main factory in every UNO application. It instantiates services by their service name, to enumerate all implementations of a certain service, and to add or remove factories for a certain service at runtime. The service manager is passed to every UNO component during instantiation.

XMultiServiceFactory Interface

The main interface of the service manager is the com.sun.star.lang.XMultiServiceFactory interface. It offers three methods: createInstance(), createInstanceWithArguments() and getAvailableServiceNames().

interface XMultiServiceFactory: com::sun::star::uno::XInterface 

{  

    com::sun::star::uno::XInterface createInstance( [in] string aServiceSpecifier )

        raises( com::sun::star::uno::Exception );

 

    com::sun::star::uno::XInterface createInstanceWithArguments(

            [in] string ServiceSpecifier,

            [in] sequence<any> Arguments )

        raises( com::sun::star::uno::Exception );

 

    sequence<string> getAvailableServiceNames();

};  

When using the service name, the caller does not have any influence on which concrete implementation is instantiated. If multiple implementations for a service exist, the service manager is free to decide which one to employ. This in general does not make a difference to the caller because every implementation does fulfill the service contract. Performance or other details may make a difference. So it is also possible to pass the implementation name instead of the service name, but it is not advised to do so as the implementation name may change.

In case the service manager does not provide an implementation for a request, a null reference is returned, so it is mandatory to check. Every UNO exception may be thrown during instantiation. Some may be described in the specification of the service that is to be instantiated, for instance, because of a misconfiguration of the concrete implementation. Another reason may be the lack of a certain bridge, for instance the Java-C++ bridge, in case a Java component shall be instantiated from C++ code. 

XContentEnumerationAccess Interface

The com.sun.star.container.XContentEnumerationAccess interface allows the creation of an enumeration of all implementations of a concrete servicename.

interface XContentEnumerationAccess: com::sun::star::uno::XInterface 

{  

    com::sun::star::container::XEnumeration createContentEnumeration( [in] string aServiceName );

 

    sequence<string> getAvailableServiceNames();

 

};  

The createContentEnumeration() method returns a com.sun.star.container.XEnumeration interface. Note that it may return an empty reference in case the enumeration is empty.

interface XEnumeration: com::sun::star::uno::XInterface 

{  

    boolean hasMoreElements();

 

    any nextElement()

        raises( com::sun::star::container::NoSuchElementException,

                com::sun::star::lang::WrappedTargetException );

 

};  

In the above case, the returned any of the method Xenumeration.nextElement() contains a com.sun.star.lang.XSingleServiceFactory interface for each implementation of this specific service. You can, for instance, iterate over all implementations of a certain service and check each one for additional implemented services. The XSingleServiceFactory interface provides such a method. With this method, you can instantiate a feature rich implementation of a service.

XSet Interface

The com.sun.star.container.XSet interface allows the insertion or removal of com.sun.star.lang.XSingleServiceFactory or com.sun.star.lang.XSingleComponentFactory implementations to the service manager at runtime without making the changes permanent. When the office application terminates, all the changes are lost. The object must also support the com.sun.star.lang.XServiceInfo interface that provides information about the implementation name and supported services of the component implementation.