Uno/Article/Working with Environments, Mappings & Objects

From Apache OpenOffice Wiki
< Uno
Revision as of 15:36, 9 August 2006 by Kr (Talk | contribs)

Jump to: navigation, search

Working with Environments, Mappings & Objects

Environments, mappings and objects are at the heart of Uno. Understanding their relationship and how to use them is fundamental for understanding how Uno

  • integrates different programming languages,
  • achieves remote transparency,
  • allows to trace one or multiple components objects,
  • transparently accesses objects implemented in other programming languages,
  • provide well directed backdoors for optimization purposes,
  • can implicitly handle contexts (such as the "ComponentContext"),
  • protect thread-unsafe objects,
  • isolate thread-affine objects,
  • and more.

Theory

The three entities are

  • environments for managing objects,
  • mappings, basically connecting environments, to bring objects from one world to another,
  • and certainly objects, for doing implementations.

All three (environments, mappings, objects) are dynamically extendable.

Environments

Environments are at the fundament of Uno. Environments

  • manage a set of objects, typically sharing some characteristics as the ABI or the "purpose", and
  • control the life cycle of any particular object belonging to an environment.

Examples for environments are:

  • "uno" - the environment managing Binary Uno standard ABI objects,
  • "gcc3" - the environment managing GCC3 C++ ABI objects,
  • "uno:unsafe" - the environment managing Binary Uno standard ABI thread unsafe objects,
  • "jni:affine" - the environment managing JNI ABI thread affine objects.

The part in front of the colon being the ABI, and the part behind it being the purpose (note: purposes may be chained, e.g. "<ABI>:unsafe:debug"). The whole string being the environment descriptor.

Variants

While their is no difference in general, we differentiate environments to either be

ABI

ABI environments do not control any state and are solely managing objects of their particular ABI.

Purpose

Purpose environments typically only manage objects of the standard (in Binary Uno the "UNO") ABI, but do control state. Purpose environments alter their when being activated.

Life Cycle

Every environment is process wide unique and global. Requesting a particular environment (e.g. "uno:unsafe") multiple times, always returns the same instance. An environments identity is uniquely derived from its descriptionn. An environment exists as long as an object or proxy is registered.

Activation

Environments may be activated either directly e.g. by calling enter, or indirectly by calling invoke. Depending on the particular environment, semantics might differ slightly. Environments may control some global state, which they may alter in response to activation.

As only purpose environments control any state, their is actually no difference between activating two environments with different ABIs but with the same purpose:

  "gcc3:unsafe"
  "uno:unsafe"

Consequently, entering a "pure" ABI environment, such as "gcc3", has no effect at all.

Integrity

Direct manipulation (e.g. casting and calling) of an object of a particular environment must only be done, while the owning environment has been activated. In contrast, indirect manipulation through the owning environment is guaranteed to always be safe.

Objects of different environments, even having the same ABI, must never be mixed, otherwise environment integrity may break (e.g. leading to a thread-safe object providing a thread-unsafe object to multiple threads).

Substitution

Environments may be substituted with compatible environments. Environments are compatible if the to be substituted environments descriptor is a prefix of the substitutes descriptor, e.g.

"uno:unsafe" 

may be substituted with

"uno:unsafe:debug"
Naming

Environment naming has not been implemented yet. Naming would allow to have multiple environments of the same name, e.g.

"gcc3:unsafe;impl_one"

or

"gcc3:unsafe;impl_two"

This would allow e.g. to selectively load components into different environments, e.g. being able to log any inter-environment calls.

Parameters

Environment parameters have not been implemented yet. Environment parameters are essential for things as simplifying remote access (see Uno/Todo#Features). Environment parameters may look like this:

"remote[socket,host=0,port=12345;urp]"

which would pass the string

"socket,host=0,port=12345;urp"

to the remote environment.

Environment parameters could be passed to any ABI or purpose listed in the environment descriptor, e.g.

"remote[socket,host=0,port=12345;urp]:debug[logfile=<filename>.log]"

would log all calls to the remote objects to the named log file.

Mappings

Mappings connect any two particular environments in a way, that an object of one environment may be "mapped" to another environment, actually providing a representation of the source object in terms of the destination environment.

Cascading

A mapping may not only be direct, but may very well be composed of multiple "smaller" mappings. See the cascaded mapping specification for details. E.g. if the Binary Uno runtime can not find any direct mapping, it tries to concatenate multiple mappings, e.g.

"gcc3" -> "gcc3:unsafe" 

gets actually mapped as

"gcc3" -> "uno" -> "uno:unsafe" -> "gcc3:unsafe"
Life Cycle

Mappings live as long as objects are mapped. Releasing the last mapped object also releases the mapping.

Variants

While their is no difference in general, we differentiate mappings to either mediate between different

  • ABIs, or to mediate between different
  • purposes.

As in most cases it is needed to map back and forth, mappings are implemented as bridges. Typical names for bridges are:

"uno_gcc3"
"uno_remote"
"uno_uno_unsafe"

Objects

Objects are the third stand in the Uno architecture. Where environments deal with the management of objects, while mappings know how to forward a particular object from one ABI to another, objects are the implementation parts.

Implementation

Basically, (Uno) objects may be implemented anywhere, in components, in libraries, in applications or in the network. Accessing and creating Uno objects solely through other Uno objects guarantees environment integrity. Unfortunately, only Uno components ensure the by-object-only access, while applications, libraries and network sockets may very well by-pass this. Therefor precautions need to be taken, to still ensure that environmental integrity can not break.

Specialization: Libraries as well as applications, may be implemented from environment-specialized to environment-any. Partly specialization is possible, e.g.

  • a library taking and returning "uno" objects only - this library is specialized on the "uno" environment,
  • a library taking and returning "gcc3:unsafe" objects only - this library is specialized on the "gcc3:unsafe" environment,
  • a library taking and returning "gcc3[:<purpose>]*" objects only - this library is partly specialized, namely on the ABI, only,
  • a library taking and returning "<ABI>[:<purpose>]*" objects - this library is basically able to deal with any kind of objects, it is therefor environment-any.

Mapping: Actually, any specialized library (or function) can easily be wrapped into any kind of specialization, as long as the necessary mapping is available. Hence, exactly this is what the Uno runtime does dynamically, in case Uno objects are used.

Type Safety: Depending on the capabilities of the particular programming language and language binding, correct dealing with specialization may be enforced at compilation time, e.g. by dedicated reference types. Actually, it is recommended to use the most specific type as possible, or, the other way around, to be as specific as the API only, unfortunately this is not always possible because of missing language or feature support.

An environment-any or partly -specialized library (or function) needs to request the missing information from the runtime, this may be done by invoking the getCurrentEnvironment runtime function.

Note: Unfortunately, no type safe purpose references are yet available for any Uno language binding. So, this is planned.

Note: It is planned, to support the selection of an implementation environment, determining an implementations environment at compile time, e.g. for 'C' like languages by a macro or an include. Some experiments have been done, so no final decisions have been made yet.

Life Cycle

An objects life cycle may depend on the base system, systems without garbage collection typically only support reference counting, while system with garbage collection keep objects alife as long as they are referenced.

Activation

An object may be activated, if the managing environment has been activated. Typically, a cast precedes the concrete activation of an object.

Practice

So, you may ask yourself, having read the above, what exactly can you do with this stuff?! Actually, the combination of environments, mappings and objects is very powerful. You can

  • achieve remote transparency,
  • trace one or multiple particular objects,
  • transparently access objects implemented in other programming languages,
  • provide well directed backdoors for optimization purposes,
  • implicitly handle contexts,
  • protect thread-unsafe objects,
  • and more.

More examples to come.

Environments

Mappings

Objects

C++ Example - Function always returning an appropriate object

The following example shows a function always returning an appropriate (correct ABI and purpose) object of type XInterface. For this function to work properly, the client must have activated the appropriate environment, as the uno::Reference is only partly (namely ABI) specialized.

Callee: [cpp] // This function is environment specialized on "c++<purpose>*". uno::Reference<uno::XInterface> create_appropriateObject(void) {

 uno:Reference<uno::XInterface> result_Obj;
 // We may want to open a new scope, to ensure that "result_Obj" does
 // not get destructed while "c++:unsafe" is active.
 {
   // We need to remember the callers environment, to "map-out/in"
   // the parameters and return values properly.
   uno::Environment outerEnv(uno::getCurrent());
   // We activate (enter) the "c++:unsafe" environment.
   // Note: Any other environment suiteable for "MyUnsafeObject" would work as well.
   cppu::EnvGuard unsafeGuard(uno::Environment(rtl::OUString(RTL_CONSTASCII_PARAM("c++:unsafe"))));
   // This reference points to a "thread-unsafe" object.
   Reference<uno::XInterface> unsafeEnv_Obj(new MyUnsafeObject());
   // We may do some activations on "unsafeEnv_Obj".
   unsafeEnv_Obj->doThis();
   unsafeEnv_Obj->doThat();
   // We "mapOut" the object and assign it to "result_Obj".
   result_Obj.set(cppu::mapOut(unsafeEnv_Obj, outerEnv), SAL_NO_ACQUIRE);
   // We may _not_ activate result_obj, as we are still in the "c++:unsafe" environment.
 }
 // Using "result_obj" is "safe" here.
 return result_Obj;

}

Caller: [cpp] ... {

 // Whatever "c++<purpose>*" we enter, the result of "create_appropriateObject" will 
 // always match.
 cppu::EnvGuard cppDebug_Guard(rtl::OUString(RTL_CONSTASCII_PARAM("c++:debug")));
 uno::Reference obj(create_appropriateObject());

} ...

Personal tools