[ Previous document | Content Table | Next document ]

# 5 Advanced UNO

## 5.1 Choosing an Implementation Language

The UNO technology provides a framework for cross-platform and language independent programming. All the OpenOffice.org components can be implemented in any language supported by UNO, as long as they only communicate with other components through their IDL interfaces.

| Note: The condition "as long as they only communicate with other components through their IDL interfaces" is to be strictly taken. In fact, a lot of implementations within OpenOffice.org export UNO interfaces and still use private C++ interfaces. This is a tribute to older implementations that cannot be rewritten in an acceptable timeframe.
--- | ---

A developer can customize the office to their needs with this flexibility, but they will have to decide which implementation language should be selected for a specific problem.

### 5.1.1 Supported Programming Environments

The support for programming languages in UNO and OpenOffice.org is divided into three different categories.

1. Languages that invoke calls on existing UNO objects are possibly implemented in other programming languages. Additionally, it may be possible to implement certain UNO interfaces, but not UNO components that can be instantiated by the service manager.

2. Languages that implement UNO components. UNO objects implemented in such a language are accessible from any other language that UNO supports, just by instantiating a service by name at the servicemanager. For instance, the developer can implement a OpenOffice.org Calc addin (see 8 Spreadsheet Documents).

3. Languages that are used to write code to be delivered within OpenOffice.org documents and utilize dialogs designed with the OpenOffice.org dialog editor.

The following table lists programming languages currently supported by UNO. 'Yes' in the table columns denotes full support, 'no' denotes that there is no support and is not even planned in the future. 'Maybe in future' means there is currently no support, but this may change with future releases.

Language | UNO scripting | UNO components | Deployment with OpenOffice.org documents
--- | --- | --- | ---
C++ | yes | yes | no
C | maybe in future | maybe in future | no
Java | yes | yes | maybe in future
StarBasic | yes | no | yes
OLE automation (win32 only) | yes | maybe in future | maybe in future
Python | maybe in future (under development) | maybe in future (under development) | maybe in future

#### Java

Java is a an accepted programming language offering a standard library with a large set of features and available extensions. Additional extensions will be available in the future, such as JAX-RPC for calling webservices. It is a typesafe language with a typesafe UNO binding. Although interfaces have to be queried explicitly, the type safety makes it suitable for larger projects. UNO components can be implemented with Java, that is, the Java VM is started on demand inside the office process when a Java component is instantiated. The OfficeBean allows embedding OpenOffice.org documents in Java Applets and Applications.

There is a constant runtime overhead of about 1 to 2 ms per call that is caused by the bridge conversion routines when calling UNO objects implemented in other language bindings. Since OpenOffice.org consists of C++ code, every Java call into the office needs to be bridged. This poses no problems if there are a few calls per user interaction. The runtime overhead will hurt the application when routines produce hundreds or thousands of calls.

#### C++

C++ is an accepted programming language offering third-party products. In addition to C++ being fast since it is compiled locally, it offers the fastest communication with OpenOffice.org because most of the essential parts of office have been developed in C++. This advantage becomes less important as you call into the office through the interprocess bridge, because every remote call means a constant loss of 1 to2 ms. The fastest code to extend the office can be implemented as a C++ UNO component. It is appropriate for larger projects due to its strong type safety at compile time.

C++ is difficult to learn and coding, in general, takes longer, for example, in Java. The components must be built for every platform, that leads to a higher level of complexity during development and deployment.

#### OpenOffice.org Basic

OpenOffice.org Basic is the scripting language developed for and integrated directly into OpenOffice.org. It currently offers the best integration with OpenOffice.org, because you can insert code into documents, attach arbitrary office events, such as document loading, keyboard shortcuts or menu entries, to Basic code and use dialogs designed within the OpenOffice.org IDE. In Basic, calls are invoked on an object rather than on a specific interface. Interfaces, such as com.sun.star.beans.XPropertySet are integrated as Basic object properties. Basic always runs in the office process and thus avoids costly interprocess calls.

The language is type unsafe, that is, only a minimal number of errors are found during compilation. Most errors appear at runtime, therefore it is not the best choice for large projects. The language is OpenOffice.org specific and only offers a small set of runtime functionality with little third-party support. All office functionality is used through UNO. UNO components cannot be implemented with Basic. The only UNO objects that can be implemented are listeners. Finally, Basic does not offer any thread support.

#### OLE Automation Bridge

The OLE Automation bridge opens the UNO world to programming environments that support OLE automation, such as Visual Basic, JScript, Delphi or C++ Builder. Programmers working on the Windows platform can write programs for OpenOffice.org without leaving their language by learning a new API. These programmers have access to the libraries provided by their language. It is possible to implement UNO objects, if the programming language supports object implementation.

This bridge is only useful on a Win32 machine, thereby being a disadvantage . Scripts always run in a different process so that every UNO call has at least the usual interprocess overhead of 1 to 2 ms. Currently Automation UNO components cannot be implemented for the service manager, but this may change in the future.

#### Python

A Python scripting bridge (PyUNO) is currently developed by Ralph Thomas. It is available in an experimental alpha state with known limitations. For details, see PyUNO on udk.openoffice.org.

### 5.1.2 Use Cases

The following list gives typical UNO applications for the various language environments.

#### Java

- Servlets creating Office Documents on the fly, Java Server Pages

- Server-Based Collaboration Platforms, Document Management Systems

- Calc add-ins

- Chart add-ins

- Database Drivers

#### C++

- Filters reading document data and generating Office Documents through UNO calls

- Database Drivers

- Database Drivers

- Calc add-ins

- Chart add-ins

#### OpenOffice.org Basic

- Office Automation

- Event-driven data-aware forms

#### OLE Automation

- Office Automation, creating and controlling Office Documents from other applications and from Active Server Pages

#### Python

- Calc add-ins

### 5.1.3 Recommendation

All languages have their advantages and disadvantages as previously discussed , but there is not one language for all purposes, depending on your use. Consider carefully before starting a new project and evaluate the language to use so that it saves you time.

Sometimes it may be useful to use multiple languages to gain the advantages of both languages. For instance, currently it is not possible to attach a keyboard event to a java method, therefore, write a small basic function, which forwards the event to a java component.

The number of languages supported by UNO may increase and some of the limitations shown in the table above may disappear.

## 5.2 Language Bindings

UNO language bindings enable developers to use and implement UNO objects in arbitrary programming languages. Thus, the existing language bindings connect between implementation environments, such as Java, C++, OpenOffice.org Basic and OLE Automation. The connection is accomplished by bridges. The following terms are used in our discussion about the implementation of language bindings.

In our context, the target language or target environment denotes the language or environment from which the UNO component model is accessed. The bridging language is the language used for writing the bridge code.

An object-oriented language determines the layout of its objects in memory. We call an object that is based on this layout a language object. The layout along with everything that relates to it, such as creation, destruction, and interaction, is the object model of a language.

A UNO proxy (short: proxy) is created by a bridge and it is a language object that represents a UNO object in the target language. It provides the same functionality as the original UNO object. There are two terms which further specialize a UNO proxy. The UNO interface proxy is a UNO proxy representing exactly one interface of a UNO object, whereas a UNO object proxy represents an uno object with all its interfaces.

An interface bridge bridges one UNO interface to one interface of the target language, that is, to a UNO interface proxy. When the proxy is queried for another interface that is implemented by the UNO object, then another interface proxy is returned. In contrast, an object bridge bridges entire UNO objects into UNO object proxies of the target language. The object proxy receives calls for all interfaces of the UNO object.

### 5.2.1 Implementing UNO Language Bindings

This section introduces the basic steps to create a new language binding. The steps required depend on the target language. The section provides an overview of existing language bindings to help you to decide what is necessary for your case. It is recommended that you read the sources for available language bindings and transfer the solutions they offer to the new circumstances of your target language.

#### Overview of Language Bindings and Bridges

Creating a language binding for UNO involves the following tasks:

##### Language Specification and UNO Feature Support

When writing a language binding, consider how to map UNOIDL types to your target language, treat simple types and handle complex types, such as struct, sequence, interface and any. Furthermore, UNOIDL features, such as services, properties and exceptions must be matched to the capabilities of the target language and accommodated, if need be.

##### Code Generator

If the target language requires type definitions at compile time, a code generator must translate UNOIDL type definitions to the target language type definitions according to the language specification, so that the types defined in UNOIDL can be used.

##### UNO Bridge

UNO communication is based on calls to interfaces. Bridges supply the necessary means to use interfaces of UNO objects between implementation environments. The key for bridging is an intermediate environment called binary UNO,that consists of binary data formats for parameters and return values, and a C dispatch method used to call arbitrary operations on UNO interfaces. A bridge must be capable of the following tasks:

- Between the target language and OpenOffice.org:

- a) Converting operation parameters from the target language to binary UNO.

- b) Transforming operation calls in the target language to calls in binary UNO in a different environment.

- c) Transporting the operation call with its parameters to OpenOffice.org and the return values back to the target language.

- d) Mapping return values from binary UNO to the target language.

- Between OpenOffice.org and the target language, that is, during callbacks or when using a component in the target language:

- a) Converting operation parameters from binary UNO to the target language.

- b) Transforming operation calls in binary UNO to calls in the target language.

- c) Transporting the operation call with its parameters to the target language and the return values back to OpenOffice.org.

- d) Converting return values from the target language to binary UNO.

The Reflection API delivers information about UNO types and is used by bridges to support type conversions (com.sun.star.script.Converter), and method invocations (com.sun.star.script.Invocation and com.sun.star.script.XInvocation). Furthermore, it supplies runtime type information and creates instances of certain UNO types, such as structs (com.sun.star.reflection.CoreReflection).

##### UNO Component Loader

An implementation loader is required to load and activate code produced by the target language if implementations in the target language are to be instantiated. This involves locating the component files produced by the target language, and mechanisms to load and execute the code produced by the target language, such as launching a runtime environment. Currently there are implementation loaders for jar files and locally shared libraries on the platforms supported by UNO.

##### Bootstrapping

A UNO language binding must prepare itself so that it can bridge to the UNO environments. It depends on the target environment how this is achieved. In Java, C++, and Python, a local service manager in the target environment is used to instantiate a com.sun.star.bridge.UnoUrlResolver that connects to OpenOffice.org. In the Automation bridge, the object com.sun.star.ServiceManager is obtained from the COM runtime system and in OpenOffice.org Basic the service manager is available from a special method of the Basic runtime environment, getProcessServiceManager().

#### Implementation Options

There are two different approaches when creating a UNO language binding.

1. Programming languages checking types at compile time.
   Examples are the languages Java or C++. In these environments, it is necessary to query for interfaces at certain objects and then invoke calls compile-time-typesafe on these interfaces.

2. Programming languages checking types at runtime.
   Examples are the languages StarBasic, Python or Perl. In these languages, the interfaces are not queried explicitly as there is no compiler to check the signature of a certain method. Instead, methods are directly invoked on objects. During execution, the runtime engine checks if a method is available at one of the exported interfaces, and if not, a runtime error is raised. Typically, such a binding has a slight performance disadvantage compared to the solution above.

You can achieve different levels of integration with both types of language binding.

1. Call existing UNO interfaces implemented in different bindings.
   This is the normal scripting use case, for example, connect to a remote running office, instantiate some services and invoke calls on these services (unidirectional binding).

2. Implement UNO interfaces and let them be called from different bindings.
   In addition to 1) above, a language binding is able to implement UNO interfaces, for example, for instance listener interfaces, so that your code is notified of certain events (limited bidirectional binding).

3. Implement a UNO component that is instantiated on demand from any other language at the global service manager.
   In addition to 2) above, a binding must provide the code which starts up the runtime engine of the target environment. For example, when a Java UNO component is instantiated by the OpenOffice.org process, the Java VM must be loaded and initialized, before the actual component is loaded (bidirectional binding).

A language binding should always be bidirectional. That is, it should be possible to access UNO components implemented in the target language from OpenOffice.org, as well as accessing UNO components that are implemented in a different language from the ta rget language.

The following table provides an overview about the capabilities of the different language bindings currently available for OpenOffice.org:

Language | scripting (accessing office objects) | interface implementation | component development
--- | --- | --- | ---
C++ (platform dependent) | yes | yes | yes
Java | yes | yes | yes
StarBasic | yes | (only listener interfaces) | no
OLE automation (Win32 only) | yes | yes | no (maybe in the future)

The next section outlines the implementation of a C++ language binding. The C++ binding itself is extremely platform and compiler dependent, which provides a barrierwhen porting OpenOffice.org to a new platform. Although this chapter focuses on C++ topics, the chapter can be applied for other typesafe languages that store their code in a shared library, for instance, Delphi, because the same concepts apply.

The section 5.2.3 Advanced UNO - Language Bindings - UNO Reflection API considers the UNO reflection and invocation API, which offers generic functionality to inspect and call UNO objects. The section 5.2.4 Advanced UNO - Language Bindings - XInvocation Bridge explains how the Reflection API is used to implement a runtime type-checking language binding.

The final chapter 5.2.5 Advanced UNO - Language Bindings - Implementation Loader briefly describes the concept of implementation loaders that instantiates components on demand independently of the client and the implementation language.The integration of a new programming language into the UNO component framework is completed once you have a loader.

### 5.2.2 UNO C++ bridges

This chapter focuses on writing a UNO bridge locally, specifically writing a C++ UNO bridge to connect to code compiled with the C++ compiler. This is an introduction for bridge implementers.. It is assumed that the reader has a general understanding of compilers and a of 80x86 assembly language. Refer to the section 5.2.5 Advanced UNO - Language Bindings - Implementation Loader for additional information.

#### Binary UNO Interfaces

A primary goal when using a new compiler is to adjust the C++-UNO data type generator (cppumaker tool) to produce binary compatible declarations for the target language. The tested cppu core functions can be used when there are similar sizes and alignment of UNO data types. The layout of C++ data types, as well as implementing C++-UNO objects is explained in 3.4.2 Professional UNO - UNO Language Bindings - UNO C++ Binding.

When writing C++ UNO objects, you are implementing UNO interfaces by inheriting from pure virtual C++ classes, that is, the generated cppumaker classes (see .hdl files). When you provide an interface, you are providing a pure virtual class pointer. The following paragraph describes how the memory layout of a C++ object looks.

A C++-UNO interface pointer is always a pointer to a virtual function table (vftable), that is, a C++ this pointer. The equivalent binary UNO interface is a pointer to a struct _uno_Interface that contains function pointers. This struct holds a function pointer to a uno_DispatchMethod() and also a function pointer to acquire() and release():

// forward declaration of uno_DispatchMethod()

typedef void (SAL_CALL * uno_DispatchMethod)(

struct _uno_Interface * pUnoI,

const struct _typelib_TypeDescription * pMemberType,

void * pReturn,

void * pArgs[],

uno_Any ** ppException );

// Binary UNO interface

typedef struct _uno_Interface

{

/** Acquires uno interface.

@param pInterface uno interface

*/

void (SAL_CALL * acquire )( struct _uno_Interface * pInterface );

/** Releases uno interface.

@param pInterface uno interface

*/

void (SAL_CALL * release )( struct _uno_Interface * pInterface );

/** dispatch function

*/

uno_DispatchMethod pDispatcher ;

} uno_Interface;

Similar to com.sun.star.uno.XInterface, the life-cycle of an interface is controlled using the acquire() and release() functions of the binary UNO interface. Any other method is called through the dispatch function pointer pDispatcher. The dispatch function expects the binary UNO interface pointer (this), the interface member type of the function to be called, an optional pointer for a return value, the argument list and finally a pointer to signal an exception has occurred.

The caller of the dispatch function provides memory for the return value and the exception holder (uno_Any).

The pArgs array provides pointers to binary UNO values, for example, a pointer to an interface reference (_uno_Interface **) or a pointer to a SAL 32 bit integer (sal_Int32 *).

A bridge to binary UNO maps interfaces from C++ to binary UNO and conversely. To achieve this, implement a mechanism to produce proxy interfaces for both ends of the bridge.

#### C++ Proxy

A C++ interface proxy carries its interface type (reflection), as well as its destination binary UNO interface (this pointer). The proxy's vftable pointer is patched to a generated vftable that is capable of determining the index that was called ,as well as the this pointer of the proxy object to get the interface type.

The vftable requires an assembly code. The rest is programmed in C/C++. You are not allowed to trash the registers. On many compilers, the this pointer and parameters are provided through stack space. The following provides an example of a Visual C++ 80x86:

vftable slot0:

mov eax, 0

jmp cpp_vftable_call

vftable slot0:

mov eax, 1

jmp cpp_vftable_call

vftable slot0:

mov eax, 2

jmp cpp_vftable_call

...

static __declspec(naked) void __cdecl cpp_vftable_call(void)

{

__asm

{

sub esp, 8 // space for immediate return type

push esp

push eax // vtable index

mov eax, esp

add eax, 16

push eax // original stack ptr

call cpp_mediate // proceed in C/C++

add esp, 12

// depending on return value, fill registers

cmp eax, typelib_TypeClass_FLOAT

je Lfloat

cmp eax, typelib_TypeClass_DOUBLE

je Ldouble

cmp eax, typelib_TypeClass_HYPER

je Lhyper

cmp eax, typelib_TypeClass_UNSIGNED_HYPER

je Lhyper

// rest is eax

pop eax

add esp, 4

ret

Lhyper: pop eax

pop edx

ret

Lfloat: fld dword ptr [esp]

add esp, 8

ret

Ldouble: fld qword ptr [esp]

add esp, 8

ret

}

}

The vftable is filled with pointers to the different slot code (snippets). The snippet code recognizes the table index being called and calls cpp_vftable_call(). That function calls a C/C++ function (cpp_mediate()) and sets output registers upon return, for example, for floating point numbers depending on the return value type.

Remember that the vftable handling described above follows the Microsoft calling convention, that is, the this pointer is always the first parameter on the stack. This is currently not the case for gcc that prepends a pointer to a complex return value before the this pointer on the stack if a method returns a struct. This complicates the (static) vftable treatment, because different vftable slots have to be generated for different interface types, adjusting the offset to the proxy this pointer:

Microsoft Visual C++ call stack layout (esp offset [byte]):
0: return address
4: this pointer
8: optional pointer, if return value is complex (i.e. struct to be copy-constructed)
12: param0
16: param1
20: ...

This is usually the hardest part for stack-oriented compilers. Afterwards proceed in C/C++ (cpp_mediate()) to examine the proxy interface type, read out parameters from the stack and prepare the call on the binary UNO destination interface.

Each parameter is read from the stack and converted into binary UNO. Use cppu core functions if you have adjusted the cppumaker code generation (alignment, sizes) to the binary UNO layout (see cppu/inc/uno/data.h).

After calling the destination uno_dispatch() method, convert any out/inout and return the values back to C++-UNO, and return to the caller. If an exception is signalled (*ppException != 0), throw the exception provided to you in ppException. In most cases, you can utilize Runtime Type Information (RTTI) from your compiler framework to throw exceptions in a generic manner. Disassemble code throwing a C++ exception, and observe what the compiler generates.

#### Binary UNO Proxy

The proxy code is simple for binary UNO. Convert any in/inout parameters to C++-UNO values, preparing a call stack. Then perform a virtual function call that is similar to the following example for Microsoft Visual C++:

void callVirtualMethod(

void * pThis, sal_Int32 nVtableIndex,

void * pRegisterReturn, typelib_TypeClass eReturnTypeClass,

sal_Int32 * pStackLongs, sal_Int32 nStackLongs )

{

// parameter list is mixed list of * and values

// reference parameters are pointers

__asm

{

mov eax, nStackLongs

test eax, eax

je Lcall

// copy values

mov ecx, eax

shl eax, 2 // sizeof(sal_Int32) == 4

add eax, pStackLongs // params stack space

Lcopy: sub eax, 4

push dword ptr [eax]

dec ecx

jne Lcopy

Lcall:

// call

mov ecx, pThis

push ecx // this ptr

mov edx, [ecx] // pvft

mov eax, nVtableIndex

shl eax, 2 // sizeof(void *) == 4

add edx, eax

call [edx] // interface method call must be __cdecl!!!

// register return

mov ecx, eReturnTypeClass

cmp ecx, typelib_TypeClass_VOID

je Lcleanup

mov ebx, pRegisterReturn

// int32

cmp ecx, typelib_TypeClass_LONG

je Lint32

cmp ecx, typelib_TypeClass_UNSIGNED_LONG

je Lint32

cmp ecx, typelib_TypeClass_ENUM

je Lint32

// int8

cmp ecx, typelib_TypeClass_BOOLEAN

je Lint8

cmp ecx, typelib_TypeClass_BYTE

je Lint8

// int16

cmp ecx, typelib_TypeClass_CHAR

je Lint16

cmp ecx, typelib_TypeClass_SHORT

je Lint16

cmp ecx, typelib_TypeClass_UNSIGNED_SHORT

je Lint16

// float

cmp ecx, typelib_TypeClass_FLOAT

je Lfloat

// double

cmp ecx, typelib_TypeClass_DOUBLE

je Ldouble

// int64

cmp ecx, typelib_TypeClass_HYPER

je Lint64

cmp ecx, typelib_TypeClass_UNSIGNED_HYPER

je Lint64

jmp Lcleanup // no simple type

Lint8:

mov byte ptr [ebx], al

jmp Lcleanup

Lint16:

mov word ptr [ebx], ax

jmp Lcleanup

Lfloat:

fstp dword ptr [ebx]

jmp Lcleanup

Ldouble:

fstp qword ptr [ebx]

jmp Lcleanup

Lint64:

mov dword ptr [ebx], eax

mov dword ptr [ebx+4], edx

jmp Lcleanup

Lint32:

mov dword ptr [ebx], eax

jmp Lcleanup

Lcleanup:

// cleanup stack

mov eax, nStackLongs

shl eax, 2 // sizeof(sal_Int32) == 4

add eax, 4 // this ptr

add esp, eax

}

}

First stack data is pushed to the stack., including a this pointer, then the virtual function's pointer is retrieved and called. When the call returns, the return register values are copied back. It is also necessary to catch all exceptions generically and retrieve information about type and data of a thrown exception. In this case, look at your compiler framework functions also.

#### Additional Hints

Every local bridge is different, because of the compiler framework and code generation and register allocation. Before starting, look at your existing bridge code for the processor, compiler, and the platform in module bridges/source/cpp_uno that is part of the OpenOffice.org source tree on www.openoffice.org.

Also test your bridge code extensively and build the module cppu with debug symbols before implementing the bridge, because cppu contains alignment and size tests for the compiler.

For quick development, use the executable build in cppu/test raising your bridge library, doing lots of calls with all kinds of data on mapped interfaces.

Also test your bridge in a non-debug build. Often, bugs in assembly code only occur in non-debug versions, because of trashed registers. In most cases, optimized code allocates or uses more processor registers than non-optimized (debug) code.

### 5.2.3 UNO Reflection API

This section describes the UNO Reflection API. This API includes services and interfaces that can be used to get information about interfaces and objects at runtime.

#### XTypeProvider Interface

The interface com.sun.star.lang.XTypeProvider allows the developer to retrieve all types provided by an object. These types are usually interface types and the XTypeProvider interface can be used at runtime to detect which interfaces are supported by an object. This interface should be supported by every object to make it scriptable from OpenOffice.org Basic.

#### Converter Service

The service com.sun.star.script.Converter supporting the interface com.sun.star.script.XTypeConverter provides basic functionality that is important in the reflection context. It converts values to a particular type. For the method com.sun.star.script.XTypeConverter:convertTo(), the target type is specified as type, allowing any type available in the UNO type system. The method com.sun.star.script.XTypeConverter:convertToSimpleType() converts a value into a simple type that is specified by the corresponding com.sun.star.uno.TypeClass. If the requested conversion is not feasible, both methods throw a com.sun.star.script.CannotConvertException.

#### CoreReflection Service

The service com.sun.star.reflection.CoreReflection supporting the interface com.sun.star.reflection.XIdlReflection is an important entry point for the Uno Reflection API. The XIdlReflection interface has two methods that each return a com.sun.star.reflection.XIdlClass interface for a given name (method forName()) or any value (method getType()).

The interface XIdlClass is one of the central interfaces of the Reflection API. It provides information about types, especially about class or interface, and struct types. Besides general information, for example, to check type identity through the method equals() or to determine a type or class name by means of the method getName(), it is possible to ask for the fields or members, and methods supported by an interface type (method getFields() returning a sequence of XIdlField interfaces and method getMethods() returning a sequence of XIdlMethod interfaces).

The interface XIdlField is deprecated and should not be used. Instead the interface com.sun.star.reflection.XIdlField2 is available by querying it from an XIdlField interface returned by an XIdlClass method.

The interface XIdlField or XIdlField2 represents a struct member of a struct or get or set accessor methods of an interface type. It provides information about the field (methods getType() and getAccessMode()) and reads and – if allowed by the access mode – modifies its value for a given instance of the corresponding type (methods get() and set()).

The interface XIdlMethod represents a method of an interface type. It provides information about the method (methods getReturnType(), getParameterTypes(), getParameterInfos(), getExceptionTypes() and getMode()) and invokes the method for a given instance of the corresponding type (method invoke()).

##### Introspection

The service com.sun.star.beans.Introspection supporting the interface com.sun.star.beans.XIntrospection is used to inspect an object of interface or struct type to obtain information about its members and methods. Unlike the CoreReflection service, and the XIdlClass interface ,the inspection is not limited to one interface type but to all interfaces supported by an object. To detect the interfaces supported by an object, the Introspection service queries for the XTypeProvider interface. If an object does not support this interface, the introspection does not work correctly.

To inspect an object, pass it as an any value to the inspect() method of XIntrospection. The result of the introspection process is returned as com.sun.star.beans.XIntrospectionAccess interface. This interface is used to obtain information about the inspected object. All information returned refers to the complete object as a combination of several interfaces. When accessing an object through XIntrospectionAccess, it is impossible to distinguish between the different interfaces.

The com.sun.star.beans.XIntrospectionAccess interface provides a list of all properties (method getProperties()) and methods (method getMethods()) supported by the object. The introspection maps methods matching the pattern

FooType getFoo()
setFoo(FooType)

to a property Foo of type FooType.

com.sun.star.beans.XIntrospectionAccess also supports a categorization of properties and methods. For instance, it is possible to exclude "dangerous" methods ,such as the reference counting methods com.sun.star.uno.XInterface:acquire() and com.sun.star.uno.XInterface:release() from the set of methods returned by getMethods(). When the Introspection service is used to bind a new scripting language, it is useful to block the access to functionality that could crash the entire OpenOffice.org application when used in an incorrect manner.

The XIntrospectionAccess interface does not allow the developer to invoke methods and access properties directly. To invoke methods, the invoke() method of the XIdlMethod interfaces returned by the methods getMethods() and getMethod() are used. To access properties, a com.sun.star.beans.XPropertySet interface is used that can be queried from the com.sun.star.beans.XIntrospectionAccess:queryAdapter() method. This method also provides adapter interfaces for other generic access interfaces like com.sun.star.container.XNameAccess and com.sun.star.container.XIndexAccess, if these interfaces are also supported by the original object.

##### Invocation

The service com.sun.star.script.Invocation supporting the interface com.sun.star.lang.XSingleServiceFactory provides a generic, high-level access (higher compared to the Introspection service) to the properties and methods of an object. The object that should be accessed through Introspection is passed to the com.sun.star.lang.XSingleServiceFactory:createInstanceWithArguments() method. The returned XInterface can then be queried for com.sun.star.script.XInvocation2 derived from com.sun.star.script.XInvocation.

The XInvocation interface invokes methods and access properties directly by passing their names and additional parameters to the corresponding methods (method invoke(), getValue() and setValue()). It is also possible to ask if a method or property exists with the methods hasMethod() and hasProperty().

When invoking a method with invoke(), the parameters are passed as a sequence of any values. The Invocation service automatically converts these arguments, if possible to the appropriate target types using the com.sun.star.script.Converter service that is further described below. The Introspection functionality is suitable for binding scripting languages to UNO that are not or only weakly typed.

The XInvocation2 interface extends the Invocation functionality by methods to ask for further information about the properties and methods of the object represented by the Invocation instance. It is possible to ask for the names of all the properties and methods (method getMemberNames()) and detailed information about them represented by the com.sun.star.script.InvocationInfo struct type (methods getInfo() and getInfoForName()).

Members of struct com.sun.star.script.InvocationInfo
aName | Name of the method or property.
eMemberType | Kind of the member (method or property).
PropertyAttribute | Only for property members: This field may contain zero or more constants of the com::sun::star::beans::PropertyAttribute constants group. It is not guaranteed that all necessary constants are set to describe the property completely, but a flag will be set if the corresponding characteristic really exists. For example,iIf the READONLY flag is set, the property is read only. If it is not set, the property nevertheless can be read only. This field is irrelevant for methods and is set to 0.
aType | Type of the member, when referring to methods, the return type
aParamTypes | Types of method parameters, for properties this sequence is empty
aParamModes | Mode of method parameters (in, out, inout), for properties this sequence is empty.

The Invocation service is based on the Introspection service. The XInvocation interface has a method getIntrospection() to ask for the corresponding XIntrospectionAccess interface. The Invocation implementation currently implemented in OpenOffice.org supports this, but in general, an implementation of XInvocation does not provide access to an XInvocationAccess interface.

##### InvocationAdapterFactory

The service com.sun.star.script.InvocationAdapterFactory supporting the interfaces com.sun.star.script.XInvocationAdapterFactory and com.sun.star.script.XInvocationAdapterFactory2 are used to create adapters that map a generic XInvocation interface to specific interfaces. This functionality is especially essential for creating scripting language bindings that do not only access UNO from the scripting language, but also to implement UNO objects using the scripting language. Without the InvocationAdapterFactory functionality, this would only be possible if the scripting language supported the implementation of interfaces directly.

By means of the InvocationAdapterFactory functionality it is only necessary to map the scripting language specific native invocation interface, for example, realized by an OLE IDispatch interface, to the UNO XInvocation interface. Then, any combination of interfaces needed to represent the services supported by a UNO object are provided as an adapter using the com.sun.star.script.XInvocationAdapterFactory2:createAdapter() method.

Another important use of the invocation adapter is to create listener interfaces that are passed to the corresponding add...Listener() method of an UNO interface and maps to the methods of an interface to XInvocation. In this case, usually the com.sun.star.script.XInvocationAdapterFactory:createAdapter() method is used.

##### XTypeDescription

Internally, types in UNO are represented by the type type. This type also has an interface representation com.sun.star.reflection.XTypeDescription. A number of interfaces derived from XTypeDescription represent types. These interfaces are:

- com.sun.star.reflection.XArrayTypeDescription

- com.sun.star.reflection.XCompoundTypeDescription

- com.sun.star.reflection.XEnumTypeDescription

- com.sun.star.reflection.XIndirectTypeDescription

- com.sun.star.reflection.XUnionTypeDescription

- com.sun.star.reflection.XInterfaceTypeDescription

- com.sun.star.reflection.XInterfaceAttributeTypeDescription

- com.sun.star.reflection.XInterfaceMemberTypeDescription

- com.sun.star.reflection.XInterfaceMethodTypeDescription

The corresponding services are com.sun.star.reflection.TypeDescriptionManager and com.sun.star.reflection.TypeDescriptionProvider. These services support com.sun.star.container.XHierarchicalNameAccess and asks for a type description interface by passing the fully qualified type name to the com.sun.star.container.XHierarchicalNameAccess:getByHierarchicalName() method.

The TypeDescription services and interfaces are listed here for completeness. Ordinarily this functionality would not be used when binding a scripting language to UNO, because the high-level services Invocation, Introspection and Reflection provide all the functionality required. If the binding is implemented in C++, the type type and the corresponding C API are used directly.

The following illustration provides an overview of how the described services and interfaces work together. Each arrow expresses a "uses" relationship. The interfaces listed for a service are not necessarily supported by the service directly, but contain interfaces that are strongly related to the services.

Illustration 5.1

### 5.2.4 XInvocation Bridge

#### Scripting Existing UNO Objects

This section describes UNO bridges for type-unsafe (scripting) programming languages. These bridges are based on the com.sun.star.script.Invocation service.

The most common starting point for a new scripting language binding is that you want to control OpenOffice.org from a script running externally. To accomplish this, you need to know what your scripting language offers to extend the language, for example, Python or Perl extend the language with a module concept using locally shared libraries.

In general, your bridge must offer a static method that is called from a script. Within this method, bootstrap a UNO C++ component context as described in 4.9.4 Writing UNO Components - Deployment Options for Components - Bootstrapping a Service Manager.

##### Proxying a UNO Object

Next, this component context must be passed to the script programmer, so that you can instantiate a com.sun.star.bridge.UnoUrlResolver and connect to a running office within the script.

The component context can not be passed directly as a C++ UNO reference, because the scripting engine does not recognize it, therefore build a language dependent proxy object around the C++ object Reference.

Illustration 5.2

For example, Python offers an API to create a proxy. Typically calls invoked on the proxy from a script are narrowed into one single C function. The Python runtime passes method names and an array containing the arguments to this C function.

If a proxy is implemented for a concrete interface, the method names that you received could in theory be compared to all method names offered by the UNO interface. This is not feasible, because of all the interfaces used in OpenOffice.org. The com.sun.star.script.Invocation service exists for this purpose. It offers a simple interface com.sun.star.lang.XSingleServiceFactory that creates a proxy for an arbitrary UNO object using the createInstanceWithArguments() method and passing the object the proxy acts for. Use the com.sun.star.script.XInvocation interface that is exported by this proxy to invoke a method on the UNO object.

Illustration 5.3

##### Argument Conversion

In addition, argument conversion must be c onsidered by specifying how each UNO type should be mapped to your target language.

Convert the language dependent data types to UNO data types before calling com.sun.star.script.XInvocation:invoke() and convert the UNO datatypes (return value and out parameters) to language dependent types after the call has been exectuted. The conversion routines are typically recursive functions, because data values are nested in complex types, such as struct or any.

When UNO object references are returned by method calls to UNO objects, create new language dependent proxies as discussed above. When passing a previously returned UNO object as a parameter to a new method call, the language binding must recognize that it is a proxied object and pass the original UNO object reference to the com.sun.star.script.XInvocation:invoke() call instead.

A special case for conversions are UNOIDL structs. You want to call a method that takes a struct as an argument. The first problem is the struct must be created by the bridge and the script programmer must be able to set members at the struct. One solution is that the bridge implementer creates a UNO struct using core C functions from the cppu library, but this is complicated and results in a lot of difficulty.

Therefore, a solution has been created that accesses structs through the XInvocation interface, as if they were UNO objects. This simplifies struct handling for bridge programmers. Refer to the reference documentation of com.sun.star.reflection.CoreReflection and the com.sun.star.script.Invocation service and the com.sun.star.beans.XMaterialHolder interface.

##### Exception Handling

UNO method calls may throw exceptions and must be mapped to the desired target language appropriately, depending on the capabilities of your target language. Ideally, the target language supports an exception concept, but error handlers, such as in OpenOffice.org Basic can be used also. A third way and worst case scenario is to check after every API call if an exception has been thrown,. In case the UNO object throws an excception, the XInvocation proxy throws a com.sun.star.reflection.InvocationTargetException. The exception has an additional any member, that contains the exception that was really thrown.

Note that the XInvocation proxy may throw a com.sun.star.script.CannotConvertException indicating that the arguments passed by the script programmer cannot be matched to the arguments of the desired function. For example, there are missing arguments or the types are incompatible. This must be reported as an error to the script programmer.

##### Property Support

The com.sun.star.script.Invocation has special getProperty() and setProperty() methods. These methods are used when the UNO object supports a property set and your target language, for example, supports something similar to the following:

object.propname = 'foo';.

Note that every property is also reachable by com.sun.star.script.XInvocation:invoke('setPropertyValue', ...), so these set or getProperty functions are optional.

#### Implementing UNO objects

When it is possible to implement classes in your target language, consider offering support for implementation of UNO objects. This is useful for callbacks, for example, event listeners. Another typical use case is to provide a datasource through a com.sun.star.io.XInputStream.

The script programmer determines which UNOIDL types the developed class implements, such as flagged by a special member name, for example, such as __supportedUnoTypes.

When an instance of a class is passed as an argument to a call on an external UNO object, the bridge code creates a new language dependent proxy that additionally supports the XInvocation interface. the bridge code hands the XInvocation reference of the bridge's proxy to the called object. This works as long as the com.sun.star.script.XInvocation:invoke() method is used directly, for instance OpenOffice.org Basic, except if the called object expects an XInputStream.

The com.sun.star.script.InvocationAdapterFactory service helps by creating a proxy for a certain object that implements XInvocation and a set of interfaces, for example, given by the __supportedUnoTypes variable. The proxy returned by the createAdapater() method must be passed to the called object instead of the bridge's XInvocation implementation. When the Adapter is queried for one of the supported types, an appropriate proxy supporting that interface is created.

If a UNO object invokes a call on the object, the bridge proxy's com.sun.star.script.XInvocation:invoke() method is called. It converts the passed arguments from UNO types to language dependent types and conversely using the same routines you have for the other calling direction. Finally, it delegates the call to the implementation within the script.

Illustration 5.4

It may become difficult if you do not want to start with an external scripting engine, but want to use the scripting engine inside the OpenOffice.org process instead. This must be supported by the target language. Often it is possible to load some library dynamically and access the scripting runtime engine through a C API. It should be implemented as a UNO C++ component. There are currently no generic UNO interfaces for this case, except for the com.sun.star.loader.XImplementationLoader. Define your own interfaces that best match your requirements. You might instantiate from Basic and retrieve an initial object or start a script. Future versions of OpenOffice.org may have a more comprehensive solution.

#### Example: Python Bridge PyUNO

This section provides an example of how the Python UNO bridge PyUNO bootstraps a service manager and how it makes use of the Invocation service to realize method invocation. While some parts are implementation or Python specific, the example provides a general understanding of language bindings.

The Python bridge PyUNO uses the cppu helper library to bootstrap a local service manager that is asked for a UnoUrlResolver service in Python.

In UNO.py, Python calls PyUNO.bootstrap() and receives a local component context. Note the parameter setup in that, it points to an ini file that configures the bootstrapped service manager with a type library. The file setup.ini corresponds to the uno.ini file that is used with the global service manager of the office.

import PyUNO

import os

setup_ini = 'file:///%s/setup.ini' % os.getenv ('PWD')

class UNO:

def __init__ ( self, connection='socket,host=localhost,port=2083;urp', setup=setup_ini ):

""" do the bootstrap

connection can be one or more of the following:

socket,

host = localhost | <hostname> | <ip-addr>,

port = <port>,

service = soffice,

user = <username>,

password = <password>

;urp

"""

self.XComponentContext = PyUNO.bootstrap ( setup )

self.XUnoUrlResolver, o = \

self.XComponentContext.ServiceManager.createInstanceWithContext (

'com.sun.star.bridge.UnoUrlResolver', self.XComponentContext )

self.XNamingService, o = self.XUnoUrlResolver.resolve (

'uno:%s;StarOffice.NamingService' % connection )

self.XMultiServiceFactory, o = self.XNamingService.getRegisteredObject (

'StarOffice.ServiceManager')

self.XComponentLoader, o = \

self.XMultiServiceFactory.createInstance ( 'com.sun.star.frame.Desktop' )

...

Python uses function tables to map Python to C functions. PyUNO_module.cc defines a table with the mappings for the PyUNO object. As shown in the following example, PyUNO.bootstrap() is mapped to the C function newBootstrapPyUNO():

static struct PyMethodDef PyUNOModule_methods [] =

{

{"bootstrapPyUNO", bootstrapPyUNO, 1},

{"bootstrap ", newBootstrapPyUNO , 1},

{"createIdlStruct", createIdlStruct, 1},

{"true", createTrueBool, 1},

{"false", createFalseBool, 1},

{NULL, NULL}

};

The function newBootstrapPyUNO() calls Util::bootstrap() in PyUNO_Util.cc and passes the location of the setup.ini file.

static PyObject* newBootstrapPyUNO (PyObject* self, PyObject* args)

{

char* ini_file_location;

Reference<XComponentContext> tmp_cc;

Any a;

if (!PyArg_ParseTuple (args, "s", &ini_file_location))

return NULL;

tmp_cc = Util::bootstrap (ini_file_location);

...

Util::bootstrap() uses defaultBootstrap_InitialComponentContext(iniFile) from cppuhelper/bootstrap.hxx to create a local component context and its parameter iniFile points to the setup.ini file that configures the local service manager to use applicat.rdb. This local component context instantiates services, such as the UnoUrlResolver.

Reference<XComponentContext> bootstrap (char* ini_file_location)

{

Reference<XComponentContext> my_component_context;

try

{

my_component_context = defaultBootstrap_InitialComponentContext (

OUString::createFromAscii (ini_file_location ));

}

catch (com::sun::star::uno::Exception e)

{

printf (OUStringToOString (e.Message, osl_getThreadTextEncoding ()).getStr ());

}

return my_component_context;

}

Now newBootstrapPyUNO() continues to set up a UNO proxy. It creates local instances of com.sun.star.script.Invocation and com.sun.star.script.Converter, and calls PyUNO_new(), passing the local ComponentContext, a reference to the XSingleServiceFactory interface of com.sun.star.script.Invocation and a reference to the XTypeConverter interface of com.sun.star.script.Converter.

static PyObject* newBootstrapPyUNO (PyObject* self, PyObject* args)

{

char* ini_file_location;

Reference<XComponentContext> tmp_cc;

Any a;

if (!PyArg_ParseTuple (args, "s", &ini_file_location))

return NULL;

tmp_cc = Util::bootstrap (ini_file_location) ;

Reference<XMultiServiceFactory> tmp_msf (tmp_cc->getServiceManager (), UNO_QUERY);

if (!tmp_msf.is ())

{

PyErr_SetString (PyExc_RuntimeError, "Couldn't bootstrap from inifile");

return NULL;

}

Reference<XSingleServiceFactory> tmp_ssf (tmp_msf->createInstance (

OUString (RTL_CONSTASCII_USTRINGPARAM ("com.sun.star.script.Invocation "))), UNO_QUERY);

Reference<XTypeConverter> tmp_tc (tmp_msf->createInstance (

OUString (RTL_CONSTASCII_USTRINGPARAM ("com.sun.star.script.Converter "))), UNO_QUERY);

if (!tmp_tc.is ())

{

PyErr_SetString (PyExc_RuntimeError, "Couldn't create XTypeConverter");

return NULL;

}

if (!tmp_ssf.is ())

{

PyErr_SetString (PyExc_RuntimeError, "Couldn't create XInvocation");

return NULL;

}

a <<= tmp_cc;

return PyUNO_new (a, tmp_ssf, tmp_tc) ;

}

PyUNO_new() in PyUNO.cc is the function responsible for building all Python proxies. The call to PyUNO_new() here in newBootstrapPyUno() builds the first local PyUNO proxy for the ComponentContext object a which has been returned by Util::bootstrap().

For this purpose, PyUNO_new() uses the Invocation service to retrieve an XInvocation2 interface to the ComponentContext service passed in the parameter a:

// PyUNO_new

//

// creates Python object proxies for the given target UNO interfaces

// targetInterface given UNO interface

// ssf XSingleServiceFactory interface of com.sun.star.script.Invocation service

// tc XTypeConverter interface of com.sun.star.script.Converter service

PyObject* PyUNO_new (Any targetInterface,

Reference<XSingleServiceFactory> ssf,

Reference<XTypeConverter> tc)

...

Sequence<Any> arguments (1);

Reference<XInterface> tmp_interface;

...

// put the target object into a sequence of Any for the call to

// ssf->createInstanceWithArguments()

// ssf is the XSingleServiceFactory interface of the com.sun.star.script.Invocation service

arguments[0] <<= targetInterface;

// obtain com.sun.star.script.XInvocation2 for target object from Invocation

// let Invocation create an XInvocation object for the Any in arguments

tmp_interface = ssf->createInstanceWithArguments (arguments);

// query XInvocation2 interface

Reference<XInvocation2 > tmp_invocation (tmp_interface, UNO_QUERY);

...

The Python proxy invokes methods, and creates and converts UNO types. This Python specific and involves the implementation of several functions according to the Python API.

Finally __init__() in UNO.py in the above example uses the PyUNO object to obtain a local UnoUrlResolver that retrieves the initial object from the office.

### 5.2.5 Implementation Loader

When you are raising a service by name using the com.sun.star.lang.ServiceManager service, the service manager decides an implementation name, code location and an appropriate loader to raise the code. It is commonly reading out of a persistent registry storage, for example, applicat.rdb, for this purpose. Previously, the regcomp tool has registered components into that registry during the OpenOffice.org setup. The tool uses a service called com.sun.star.registry.ImplementationRegistration for this task.

A loader knows how to load a component from a shared library, a .jar or script file and is able to obtain the service object factory for an implementation and retrieve information being written to the registry. A specific loader defines how a component implementer has to package code so that it is recognized by UNO. For instance in C++, a component is a shared library and in Java it is a .jar file. In a yet to be developed loader, the implementer of the loader has to decide, what a component is in that particular language – it might as well be a single script file.

The com.sun.star.loader.XImplementationLoader interface looks like the following:

interface XImplementationLoader: com::sun::star::uno::XInterface

{

com::sun::star::uno::XInterface activate ( [in] string implementationName,

[in] string implementationLoaderUrl,

[in] string locationUrl,

[in] com::sun::star::registry::XRegistryKey xKey )

raises( com::sun::star::loader::CannotActivateFactoryException );

boolean writeRegistryInfo ( [in] com::sun::star::registry::XRegistryKey xKey,

[in] string implementationLoaderUrl,

[in] string locationUrl )

raises( com::sun::star::registry::CannotRegisterImplementationException );

};

The locationUrl argument describes the location of the implementation file,for example, a jar file or a shared library. The implementationLoaderUrl argument is not used and is obsolete. The registry key xKey writes information about the implementations within a component into a persistent storage. Refer to 4.6.4 Writing UNO Components - C++ Component - Write Registration Info Using Helper Method for additional information.

The method writeRegistryMethod() is called by the regcomp tool to register a component into a registry.

The activate() method returns a factory com.sun.star.lang.XSingleComponentFactory for a concrete implementation name.

Illustration 5.5

The loader is often implemented in C/C++. When the loader is instantiated, it is responsible for starting up the language runtime, for example, Java VM, Python interpreter, through implementation. After starting up the runtime, the loader starts up the UNO language binding as discussed in the previous chapter, and bridge the XRegistryKey interface and the initial factory interface.

#### Shared Library Loader

This section discusses the loader for local components written in C++ that are loaded by the com.sun.star.loader.SharedLibrary service. Every type safe programming language that stores its code in shared libraries should implement the bridge with environments and mappings as discussed in chapters 5.2.1 Advanced UNO - Language Bindings - Implementing UNO Language Bindings - Overview of Language Bindings and Bridges - UNO Bridge and 5.2.2 Advanced UNO - Language Bindings - UNO C++ Bridges. These programming languages can reuse the existing loader without creating a new one.

When the shared library is mapped into the running process, for example, using osl _loadModule(), the shared library loader retrieves defined C symbols out of the library to determine the compiler that built the code . This function symbol is called component_getImplementationEnvironment(). When the code is compiled with the Microsoft Visual C++ compiler, it sets a pointer to a string called "msci", with gcc 3.0.1 a string "gcc3" which is a UNO environment type name. A UNO environment is connected with the code that runs in it, for example, the code compiled with gcc3 runs in the UNO environment with type name gcc3.

In addition to the environment type name, a UNO environment defines a context pointer. The context pointer and environment type name define a unique UNO environment. Although the context pointer is mostly null, it is required to identify the environments apart for the same type, for example, to identify different Java virtual machine environments when running a UNO object in two different Java virtual machines within the same process. Both environments have the same type name "java", but different context pointers. In local (C++) code, the context pointer is irrelevant , that is, set to null.The type name determines the UNO runtime environment.

When the loader knows the environment the code comes from, it decides if bridging is required. Bridging is needed if the loader code is compiled with a different compiler, thus running in a different environment. In this case, the loader raises a bridge to speak UNO with the component code.

The loader calls on two more functions related to the above XimplementationLoader interface. All of these symbols are C functions and have the following signatures:

extern "C" void SAL_CALL component_getImplementationEnvironment(

const sal_Char ** ppEnvTypeName, uno_Environment ** ppEnv );

extern "C" sal_Bool SAL_CALL component_writeInfo(

void * pServiceManager, void * pRegistryKey );

extern "C" void SAL_CALL component_getFactory(

const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey );

The latter two functions expect incoming C++-UNO interfaces, therefore the loader needs to bridge interfaces before calling the functions as stated above.

#### Bridges

The loader uses the cppu core runtime to map an interface, specifying the UNO runtime environment that needs the interface mapping. The cppu core runtime raises and connects the appropriate bridges, and provides a unidirectional mapping that uses underlying bidirectional bridges. Under Unix, the name of the bridge library follows the naming convention lib<SourceEnvironment>_<TargetEnvironment>., Under Windows, <SourceEnvironment>_<TargetEnvironment>.dll is used. For instance, libgcc3_uno.so is the bridge library for mappings from gcc3 to binary UNO,and msci_uno.dll maps from MS Visual C++ to binary UNO. The bridges mentioned above all bridge to binary UNO. Binary UNO is only used as an intermediate environment. In general, do not program binary UNO in clients. The purpose is to reduce the number of necessary bridge implementations. New bridges have to map only to binary UNO instead of all conceivable bridge combinations.

### 5.2.6 Help with New Language Bindings

Every UNO language binding is different, therefore only most important points were stressed, that is, those that are likely to appear in almost every language binding implementation. Object issues, such as lifetime, object identity, any handling, and bootstrapping were not discussed, because they are too language dependent. For more information on these issues, subscribe to the dev@udk.openoffice.org mailing list to discuss these issues for your programming language.

## 5.3 Differences Between UNO and Corba

This subsection discusses the differences between UNO and CORBA by providing the fundamental differences and if the different concepts could be mapped into the world of the other model. Consider the following feature comparison. The column titled "Mapping possible" states if a feature could be mapped by a (yet to be developed) generic bridge.

| UNO | CORBA | Mapping possible
--- | --- | --- | ---
multiple inheritance of interfaces | no | yes | yes
inheritance of structs | yes | no | yes
inheritance of exceptions | yes | no | yes
mandatory base interface for all interfaces | yes | no | yes
mandatory base exception for all exceptions | yes | no | yes
context of methods | no | yes | no
char | no | yes | yes
8 bit string | no | yes | yes
array | no | yes | yes
union | no | yes | yes
assigned values for enum | yes | no | yes
meta type 'type' | yes | no | yes
object identity | yes | no | no
lifetime mechanism | yes | no | no
succession of oneway calls | yes | no | no
in process communication | yes | no | no
thread identity | yes | no | no
customized calls | no | yes | yes
less code generation | yes | no | no

- Multiple Inheritance
  CORBA supports multiple inheritance of interfaces, whereas UNO only supports single inheritance.
  Mapping: Generates an additional interface with all methods and attributes of the inherited interfaces that must be implemented in addition to the other interfaces.

- Inheritance of Structs
  In contrast to CORBA, UNO supports inheritance of struct types. This is useful to define general types and more detailed subtypes.
  Mapping: Generate a struct with all members, plus all members of the inherited structs.

- Inheritance of Exceptions
  CORBA does not support inheritance for exceptions, whereas UNO does. Inheritance of exceptions allows the specification of a complex exception concept. It is possible to make fine granular concepts using the detailed exceptions in the layer where they are useful and the base exception in higher levels. The UNO error handling is based on exceptions and with inheritance of exceptions it is possible to specify 'error classes' with a base exception and more detailed errors of the same 'error class' that inherit from this base exception. On higher level APIs it is enough to declare the base exception to specify the 'error class' and it is possible to support all errors of this 'error class'.
  Mapping: Generates an exception with all members, plus all members of the inherited exceptions. This is the same solution as for structs.

- Mandatory Base Interfaces
  UNO specifies a mandatory base interface for all interfaces. This interface provides acquire() and release() functions for reference counting. The minimum life time of an object is managed by means of reference counting.

- Mandatory Base Exception
  UNO specifies a mandatory base exception for all exceptions. This base exception contains a string member Messagethat describes the reason for the exception in readable format. The base exception makes it also possible to catch all UNO exceptions separately.

- Method Context
  CORBA supports a request context. This context consists of a name-value pair which is specified for methods in UNOIDL. The context is used for describing the current state of the caller object. A request context provides additional, operation-specific information that may affect the performance of a request.

- Type char
  UNO does not support 8-bit characters. In UNO, char represents a 16-bit unicode character.
  Mapping: To support 8-bit characters it is possible to expand the TypeClass enum to support 8-bit characters and strings. The internal representation does not change anything, the TypeClass is only relevant for mapping.

- 8 bit string
  UNO does not support 8-bit strings. In UNO, string represents a 16-bit unicode string.
  Mapping: The same possibility as for char.

- Type array
  UNO does not support arrays at the moment, but is planned for the future.

- Type union
  UNO does not support unions at the moment, but is planned for the future.

- Assigned Values for enums
  UNO supports the assignment of values for enum values in IDL. This means that it is possible to use these values directly to specify or operate with the required enum value in target languages supporting this feature, for example, . C, C++.
  Mapping: Possible by using the names of the values.

## 5.4 UNO Design Patterns and Coding Styles

This chapter discusses design patterns and coding recommendations for OpenOffice.org. Possible candidates are:

- Singleton: global service manager, Desktop, UCB

- Factory: decouple specification and implementation, cross-environment instantiation, context-specific instances

- Listener: eliminate polling

- Element access: it is arguable if that is a design pattern or just an API

- Properties: solves remote batch access, but incurs the problem of compile-time type indifference

- UCB commands: universal dispatching of content specific operations

- Dispatch commands: universal dispatching of object specific operations, chain of responsibility

### 5.4.1 Double-Checked Locking

The double-checked locking idiom is sometimes used in C/C++ code to speed up creation of a single-instance resource. In a multi-threaded environment, typical C++ code that creates a single-instance resource might look like the following example:

#include "osl/mutex.hxx"

T * getInstance1()

{

static T * pInstance = 0;

::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());

if (pInstance == 0)

{

static T aInstance;

pInstance = &aInstance;

}

return pInstance;

}

A mutex guards against multiple threads simultaneously updating pInstance, and the nested static aInstance is guaranteed to be created only when first needed, and destroyed when the program terminates.

The disadvantage of the above function is that it must acquire and release the mutex every time it is called. The double-checked locking idiom was developed to reduce the need for locking, leading to the following modified function. Do not use.:

#include "osl/mutex.hxx"

T * getInstance2()

{

static T * pInstance = 0;

if (pInstance == 0)

{

::osl::MutexGuard aGuard(::osl::Mutex::getGlobalMutex());

if (pInstance == 0)

{

static T aInstance;

pInstance = &aInstance;

}

}

return pInstance;

}

This version needs to acquire and release the mutex only when pInstance has not yet been initialized, resulting in a possible performance improvement. The mutex is still needed to avoid race conditions when multiple threads simultaneously see that pInstance is not yet initialized, and all want to update it at the same time. The problem with getInstance2 is that it does not work.

Assume that thread 1 calls getInstance2 first, finding pInstance uninitialized. It acquires the mutex, creates aInstance that results in writing data into aInstance's memory, updates pInstance that results in writing data into pIntance's memory, and releases the mutex. Some hardware memory models a write the operations that transfer aInstance's and pInstance's data to main memory to be re-ordered by the processor executing thread 1. Now, if thread 2 enters getInstance2 when pInstance's data has already been written to main memory by thread 1, but aInstance's data has not been written yet (remember that write operations may be done out of order), then thread 2 sees that pInstance has already been initialized and exits from getInstance2 directly. Thread 2 dereferences pInstance thereafter, accessing aInstance's memory that has not yet been written into. Anything may happen in this situation.

In Java, double-checked locking can never be used, because it is broken and cannot be fixed.

In C and C++, the problem can be solved, but only by using platform-specific instructions, typically some sort of memory-barrier instructions. There is a macro OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER in osl/doublecheckedlocking.h that uses the double-checked locking idiom in a way that actually works in C and C++.

#include "osl/doublecheckedlocking.h"

#include "osl/mutex.hxx"

T * getInstance3()

{

static T * p = 0;

T * pInstance = p;

if (p == 0)

{

::osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());

p = pInstance;

if (p == 0)

{

static T aInstance;

p = &aInstance;

OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();

pInstance = p;

}

}

else

OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();

return p;

}

The first (inner) use of OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER ensures that aInstance's data has been written to main memory before pInstance's data is written, therefore a thread can not see pInstance to be initialized when aInstance's data has not yet reached main memory. This solves the problem described above.

The second (outer) usage of OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER is required to solve a problem concerning the reordering on Alpha processors.

| For more information about this problem, see Reordering on an Alpha processor by Bill Pugh (www.cs.umd.edu/~pugh/java/memoryModel/AlphaReordering.html) and Pattern-Oriented Software Architecture, Volume 2: Patterns for Concurrent and Networked Objects by Douglas C. Schmidt et al (Wiley, 2000). Also see the Usenet article Re:Talking about volatile and threads synchronization by Davide Butenhof (October 2002) on why the outer barrier can be moved into an else clause.
--- | ---

If you are coding in C++, there is an easier way to use double-checked locking without worrying about the fine points. Use the rtl_Instance template from rtl/instance.hxx :

#include "osl/getglobalmutex.hxx"

#include "osl/mutex.hxx"

#include "rtl/instance.hxx"

namespace {

struct Init()

{

T * operator()()

{

static T aInstance;

return &aInstance;

}

};

}

T * getInstance4()

{

return rtl_Instance< T, Init, ::osl::MutexGuard, ::osl::GetGlobalMutex >::create(

Init(), ::osl::GetGlobalMutex());

}

Note that an extra function class is required in this case. The documentation of rtl_Instance contains further examples of how this template can be used.

| If you are looking for more general information, the article The "Double-Checked Locking is Broken" Declaration (http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) is a good source on double-checked locking, while Computer Architecture: A Quantitative Approach, Third Edition by John L. Hennessy and David A. Patterson (Morgan Kaufmann, 2002) and UNIX® Systems for Modern Architectures: Symmetric Multiprocessing and Caching for Kernel Programmers by Curt Schimmel (Addison-Wesley, 1994) offer detailed information about hardware memory models.
--- | ---

[ Previous document | Content Table | Next document ]