[ Previous document | Content Table | Next document ]

15    Configuration Management

15.1    Overview

15.1.1    Capabilities

The OpenOffice.org configuration management component provides a uniform interface to get and set OpenOffice.org configuration data in an organized manner, independent of the physical data store used for the data.

Currently, the configuration API can only be used to get and set existing configuration options. You can not extend the configuration by new settings for your own purposes. For details, see 15.5 Configuration Management - Customizing Configuration Data.

15.1.2    Architecture

OpenOffice.org configuration data describes the state or environment of a UNO component or the OpenOffice.org application. There are different kinds of configuration data:

This list is not comprehensive, but indicates the range of data characteristically stored by configuration management.

The configuration management component organizes the configuration data in a hierarchical structure. The hierarchical structure and the names and data types of entries in this database are described by a schema. Only data that conforms to one of the installed schemas is stored in the database.

The hierarchical database stores any hierarchical information that can be described as a configuration schema, but it is optimized to work with the data needed for application configuration and preferences. Small data items having a well-defined data type are supported efficiently, whereas large, unspecific binary objects should not be stored in the configuration database. These objects are stored in separate files and the configuration keeps the URLs of these files only.

Configuration schemas are provided by the authors of applications and components that use the data. When a component is installed, the corresponding configuration schemas are installed into the configuration management system.

Configuration data is stored in a backend data store. In OpenOffice.org, the standard backend consists of XML files stored in a directory hierarchy. Support for more backends is planned for a future release.


Illustration 1.1: Configuration layers

For a given schema, multiple layers of data may exist that are merged together at runtime. One or more of these layers define default settings, possibly shared by several users. Additionally, there is a layer specific to a single user and contains personal preferences overriding the shared settings. In normal operations all changes to data affect only this user-specific layer.

Access to the merged configuration data for a user is managed by a com.sun.star.configuration.ConfigurationProvider object to connect to a data source, fetch and store data, and merge layers.

This provider provides views on the configuration data. A view is a subset of the entire configuration that can be navigated as an object hierarchy. The objects in this hierarchy represent nodes of the configuration hierarchy to navigate to other nodes and access values of data items.

All configuration items have a fixed type and a name.

The type is prescribed by the schema. The following kinds of items are available:

Properties hold the actual data. Group nodes form the structural skeleton defined in the schema. Set nodes are used to dynamically add and remove configuration data within the confines of the schema. Taken together, they can be used to build hierarchical structures of arbitrary complexity.

Each configuration item has a namethat uniquely identifies the item within its parent, that is, the node in the hierarchical tree that immediately contains the item under consideration. Paths spanning multiple levels of the hierarchy are built similarly to UNIX file system paths. The separator for individual name components in paths is a forward slash ('/'). Paths that begin with a slash are considered 'absolute paths' and must completely specify the location of an item within the hierarchy. Paths that start directly with a name are relative paths and describe the location of an item within one of its ancestors in the hierarchy.

The top-level subdivisions of the configuration hierarchy are called configuration modules. Each configuration module has a schema that describes the data items available within that module. Modules are the unit of schema installation. The name of a configuration module must be globally unique. The names of configuration modules have an internal hierarchical structure using a dot ('.') as a separator, similar to Java package names. The predefined configuration modules of OpenOffice.org use package names from the super-package "org.openoffice.*".

The names of container elements are specified when data items are added to a container. Data item names in the schema are limited to ASCII letters, digits and a few punctuation marks, but there are no restrictions applied to the names of container elements. This requires special handling when referring to a container element in a path. A path component addressing a container element takes the form <template-pattern>['<escaped-name>']. Here <template-pattern> can be the name of the template describing the element or an asterisk "*" to match any template. The <escaped-name> is a representation of the name of the element where a few forbidden characters are represented in an escaped form borrowed from XML. The quotes delimiting the <escaped-name> may alternatively be double quote characters "". The following character escapes are used:

Character

Escape

&

&amp;

"

&quot;

'

&apos;

In the table below, are some escaped forms for invented entries in the Set node /org.openoffice.Office.TypeDetection/Filters for (fictitious) filters:

Filter Name

Path Component

Plain Text

Filter['Plain Text']

Q & A Book

*["Q &amp; A Book"]

Bob's Filter

*['Bob&apos;s Filter']

The UIName value of the last example filter would have an absolute path of /org.openoffice.Office.TypeDetection/Filters/Filter['Bob&apos;s Filter']/UIName.

In several places in the configuration management, API arguments are passed to a newly created object instance as Sequence, for example, in the argument to com.sun.star.lang.XMultiServiceFactory:createInstanceWithArguments. Such arguments are type com.sun.star.beans.PropertyValue, and only the fields Name and Value need to be filled.


In the future ,a transition to the more appropriate argument type com.sun.star.beans.NamedValue is planned.

15.2    Object Model

The centralized entry point for configuration access is a com.sun.star.configuration.ConfigurationProvider object. This object represents a connection to a single configuration data source providing access to configuration data for a single user.

The com.sun.star.configuration.AdministrationProvider service is an extended version of this service that enables administrative access to shared configuration data.

The com.sun.star.configuration.ConfigurationProvider serves as a factory for configuration views. A configuration view provides access to the structure and data of a subset of the configuration database. This subset is accessible as a hierarchical object tree. When creating a configuration view, parameters are provided that describe the subset of the data to retrieve. In the simplest case, the only argument is an absolute configuration path that identifies a structural configuration item. This parameter is given as an argument named "nodepath". The configuration view then encompasses the sub-tree which is rooted in the indicated item.

A configuration view is not represented by a single object, but as an object hierarchy formed by all the items that are part of the selected sub-tree. The object that comes closest to representing the view as a whole is the root element of that tree. This object is the one returned by the factory method of the com.sun.star.configuration.ConfigurationProvider. In addition to the simple node-oriented interfaces, it also supports interfaces that apply to the view as a whole.


Illustration 1.2: Configuration object model overview

Within a configuration view, UNO objects with access interfaces are used to represent all structural items. Value items are not represented as objects, but retrieved as types, usually wrapped inside an any.

The following types are supported for data items:

string

Plain Text (Sequence of [printable] Unicode characters)

boolean

Boolean value (true/false)

short

16-bit integer number

int

32-bit integer number

long

64-bit integer number

double

Floating point number

binary

Sequence of uninterpreted octets

Value items contain a single value, or a sequence or array of one of the basic types. The arrays must be homogeneous, that is, mixed arrays are not supported. The configuration API treats array types as atomic items, there is no built-in support for accessing or modifying individual array elements.


Binary values should be used only for small chunks of data that cannot easily be stored elsewhere. For large BLOBs it is recommended to store links, for example, as URLs, in the configuration.

For example, bitmaps for small icons might be stored in the configuration, whereas large images are stored externally.

All of the structural objects implement the service com.sun.star.configuration.ConfigurationAccess that specifies interfaces to navigate the hierarchy and access values within the view. Different instances of this service support different sets of interfaces. The interfaces that an object supports depends on its structural type, that is, is it a group or a set, and context, that is, is it a group member, an element of a set or the root of the view.

A configuration view can be read-only or updatable. This is determined by the access requested when creating the view, but updatability may also be restricted by access rights specified in the schema or data. The basic com.sun.star.configuration.ConfigurationAccess service specifies read-only operations. If an object is part of an updatable view and is not marked read-only in the schema or the data, it implements the extended service com.sun.star.configuration.ConfigurationUpdateAccess. This service adds interfaces to change values and modify set nodes.

These service names are also used to create the configuration views. To create a view for read access, call com.sun.star.lang.XMultiServiceFactory:createInstanceWithArguments at the com.sun.star.configuration.ConfigurationProvider to request a com.sun.star.configuration.ConfigurationAccess. To obtain an updatable view, the service com.sun.star.configuration.ConfigurationUpdateAccess must be requested.

The com.sun.star.configuration.AdministrationProvider supports the same service specifiers, but creates views on shared layers of configuration data.

The object initially returned when creating a configuration view represents the root node of the view. The choice of services and interfaces it supports depends on the type of configuration item it represents. The root object has additional interfaces pertaining to the view as a whole. For example, updates of configuration data within a view are combined into batches of related changes, which exhibit transaction-like behavior. This functionality is controlled by the root object of the view.

15.3    Configuration Data Sources

Creating a view to configuration data is a two-step process.

  1. Connect to a data source by creating an instance of a com.sun.star.configuration.ConfigurationProvider for user preferences or a com.sun.star.configuration.AdministrationProvider for shared preferences.

  2. Ask the provider to create an access object for a specific nodepath in the configuration database using com.sun.star.lang.XMultiServiceFactory:createInstanceWithArguments(). The access object can be a com.sun.star.configuration.ConfigurationAccess or a com.sun.star.configuration.ConfigurationUpdateAccess.

15.3.1    Connecting to a Data Source

The first step to access the configuration database is to connect to a configuration data source.

To obtain a provider instance ask the global com.sun.star.lang.ServiceManager for a com.sun.star.configuration.ConfigurationProvider. Typically the first lines of code to get access to configuration data look similar to the following: (Config/ConfigExamples.java)

// get my global service manager

XMultiServiceFactory xServiceManager = (XMultiServiceFactory)UnoRuntime.queryInterface(

    XMultiServiceFactory.class, this.getRemoteServiceManager(

        "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager"));

final String sProviderService = "com.sun.star.configuration.ConfigurationProvider";

// create the provider and remember it as a XMultiServiceFactory

XMultiServiceFactory xProvider = (XMultiServiceFactory)

    UnoRuntime.queryInterface(XMultiServiceFactory.class,

                              xServiceManager.createInstance(sProviderService));

This code creates a default com.sun.star.configuration.ConfigurationProvider. The most important interface a com.sun.star.configuration.ConfigurationProvider implements is com.sun.star.lang.XMultiServiceFactory that is used to create further configuration objects.

The com.sun.star.configuration.ConfigurationProvider always operates in the user mode, accessing data on behalf of the current user and directing updates to the user's personal layer.

For administrative access to manipulate the default layers the com.sun.star.configuration.AdministrationProvider is used. When creating this service, additional parameters can be used that select the layer for updates or that contain credentials used to authorize administrative access. The backend that is used determines which default layers exist, how they are addressed and how administrative access is authorized. The standard file-based backend has a single shared layer only. The files for this layer are located in the share directory of the OpenOffice.org installation. To gain administrative access to this layer, no additional parameters are needed. An com.sun.star.configuration.AdministrationProvider for this backend automatically tries to read and write this shared layer. Authorization is done by the operating system based upon file access privileges. The current user requires write privileges in the shared configuration directory if an AdministrationProvider is suppose to update configuration data.

A com.sun.star.configuration.AdministrationProvider is created in the same way as a com.sun.star.configuration.ConfigurationProvider.

// get my global service manager

XMultiServiceFactory xServiceManager = getServiceManager();

// get the arguments to use

com.sun.star.beans.PropertyValue aReinitialize = new com.sun.star.beans.PropertyValue()

aReinitialize.Name = "reinitialize"

aReinitialize.Value = new Boolean(true);

Object[] aProviderArguments = new Object[1];

aProviderArguments[0] = aReinitialize;

final String sAdminService = "com.sun.star.configuration.AdministrationProvider";

// create the provider and remember it as a XMultiServiceFactory

XMultiServiceFactory xAdminProvider = (XMultiServiceFactory)

    UnoRuntime.queryInterface(XMultiServiceFactory.class,

                   xServiceManager.createInstanceWithArguments(sAdminService,aProviderArguments));

As you see in the example above, the default com.sun.star.configuration.AdministrationProvider supports a special parameter for reinitialization:

Parameter Name

Type

Default

Comments

reinitialize

boolean

false

Discard any cached information from previous runs and regenerate from scratch.

The current implementation maintains a set of cache files containing pre-parsed representations of the configuration data. If the reinitialize parameter is true, these cache files will be recreated from the XML data when the AdministrationProvider is created.

When establishing the connection, specify the parameters that select the backend to use and additional backend-specific parameters to select the data source. When there are no parameters given, the standard configuration backend and data source of the OpenOffice.org installation is used.

The standard values for these parameters may be found in the configuration file configmgr(.ini|rc) (.ini on Windows, rc on Unix) in the program directory of the OpenOffice.org installation.


The list of available backends and the parameters they support may change in a future release. Using these parameters are normally not necessary and therefore are not recommended.

The following parameter is supported to select the type of backend to use:

Parameter Name

Type

Default

Comments

servertype

string

"local"

Other values are currently not supported in OpenOffice.org.

For the "local" backend, the following parameters are used to select the location of data:

Parameter Name

Type

Default

Comments

sourcepath

string

$(installurl)/share/config/registry

The INI entry is named CFG_BaseDataURL

updatepath

string

$(userurl)/user/config/registry

The INI entry is named CFG_UserDataURL

Arguments can be provided that determine the default behavior of views created through this com.sun.star.configuration.ConfigurationProvider. The following parameters may be used for this purpose:

Parameter Name

Type

Default

Comments

locale

string

The user's locale.

lazywrite

boolean

true

The INI entry is named enable_async


After the connection is established, creating another com.sun.star.configuration.ConfigurationProvider using the same parameters may return the same object. The default configuration provider obtained when no arguments are given will always be the same object. Be careful not to call com.sun.star.lang.XComponent:dispose() on a shared com.sun.star.configuration.ConfigurationProvider.

15.3.2    Using a Data Source

After a configuration provider is obtained, call com.sun.star.lang.XMultiServiceFactory:createInstanceWithArguments() to create a view on the configuration data.

The following arguments can be specified when creating a view:

Parameter Name

Type

Default

Comments

nodepath

string

-

This parameter is required. It contains an absolute path to the root node of the view.

locale

string

The user's locale

(or "*")

Using this parameter, specify the locale to be used for selecting locale-dependent values. Use the ISO code for a locale, for example, en-US for U.S. English.

lazywrite

boolean

true

depth

integer

(unlimited)

This parameter causes the view to be truncated to a specified nesting depth.

nocache

boolean

false

This parameter is deprecated.


If the special value "*" is uused for the locale parameter, values for all locales are retrieved.
In this case, a locale-dependent property appears as a set item. The items of the set are the values for the different locales. They will have the ISO identifiers of the locales as names.

This mode is the default if you are using an com.sun.star.configuration.AdministrationProvider.

It can be used if you want to assign values for different locales in a targeted manner. Usually this is logical in an administration or installation context only.

To create a read-only view on the data, the service com.sun.star.configuration.ConfigurationAccess is requested:

// Create a specified read-only configuration view

public Object createConfigurationView(String sPath) throws com.sun.star.uno.Exception {

    // get the provider to use

    XMultiServiceFactory xProvider = getProvider();

    // The service name: Need only read access:

    final String sReadOnlyView = "com.sun.star.configuration.ConfigurationAccess";

    // creation arguments: nodepath  

    com.sun.star.beans.PropertyValue aPathArgument = new com.sun.star.beans.PropertyValue();

    aPathArgument.Name = "nodepath";

    aPathArgument.Value = sPath;

    Object[] aArguments = new Object[1];

    aArguments[0] = aPathArgument;

    // create the view

    Object xViewRoot = xProvider.createInstanceWithArguments(sReadOnlyView, aArguments);

    return xViewRoot;

}

To obtain updatable access, the service com.sun.star.configuration.ConfigurationUpdateAccess is requested. In this case, there are additional parameters available that control the caching behavior of the configuration management component:

// Create a specified updatable configuration view

Object createUpdatableView(String sPath, boolean bAsync) throws com.sun.star.uno.Exception {

    // get the provider to use

    XMultiServiceFactory xProvider = getProvider();

    // The service name: Need update access:

    final String cUpdatableView = "com.sun.star.configuration.ConfigurationUpdateAccess";

    // creation arguments: nodepath  

    com.sun.star.beans.PropertyValue aPathArgument = new com.sun.star.beans.PropertyValue();

    aPathArgument.Name = "nodepath";

    aPathArgument.Value = sPath;

    // creation arguments: commit mode - write-through or write-back  

    com.sun.star.beans.PropertyValue aModeArgument = new com.sun.star.beans.PropertyValue();

    aModeArgument.Name = "lazywrite";

    aModeArgument.Value = new Boolean(bAsync);

    Object[] aArguments = new Object[2];

    aArguments[0] = aPathArgument;

    aArguments[1] = aModeArgument;

    // create the view

    Object xViewRoot = xProvider.createInstanceWithArguments(cUpdatableView, aArguments);

    return xViewRoot;

}

A com.sun.star.configuration.AdministrationProvider supports the same service specifiers, but creates views on shared layers of configuration data. It supports additional parameters to select the exact layer to work on or to specify authorization credentials. For the standard file-based com.sun.star.configuration.AdministrationProvider, the parameters are not defined. For a com.sun.star.configuration.AdministrationProvider, the default value for the locale parameter is "*".

15.4    Accessing Configuration Data

15.4.1    Reading Configuration Data


Illustration 1.3: ConfigurationAccess services

The com.sun.star.configuration.ConfigurationAccess service is used to navigate through the configuration hierarchy and reading values. It also provides information about a node and its context.

The following example shows how to collect or display information about a part of the hierarchy. For processing elements and values, our example uses its own callback Java interface IConfigurationProcessor:

// Interface to processs information when browsing the configuration tree

public interface IConfigurationProcessor {

    // process a value item

   public abstract void processValueElement(String sPath_, Object aValue_);

    // process a structural item

   public abstract void processStructuralElement(String sPath_, XInterface xElement_);

};

Then, we define a recursive browser function:

// Internal method to browse a structural element recursively in preorder

public void browseElementRecursively(XInterface xElement, IConfigurationProcessor aProcessor)

        throws com.sun.star.uno.Exception {

    // First process this as an element (preorder traversal)

    XHierarchicalName xElementPath = (XHierarchicalName) UnoRuntime.queryInterface(

        XHierarchicalName.class, xElement);

    String sPath = xElementPath.getHierarchicalName();

    //call configuration processor object

    aProcessor.processStructuralElement(sPath, xElement);

    // now process this as a container of named elements

    XNameAccess xChildAccess =

        (XNameAccess) UnoRuntime.queryInterface(XNameAccess.class, xElement);

    // get a list of child elements

    String[] aElementNames = xChildAccess.getElementNames();

    // and process them one by one

    for (int i=0; i< aElementNames.length; ++i) {

        Object aChild = xChildAccess.getByName(aElementNames[i]);

        // is it a structural element (object) ...

        if ( aChild instanceof XInterface ) {

            // then get an interface

            XInterface xChildElement = (XInterface)aChild;

            // and continue processing child elements recursively

            browseElementRecursively(xChildElement, aProcessor);

        }

        // ... or is it a simple value

        else {

            // Build the path to it from the path of

            // the element and the name of the child

            String sChildPath;

            sChildPath = xElementPath.composeHierarchicalName(aElementNames[i]);

            // and process the value

            aProcessor.processValueElement(sChildPath, aChild);

        }

    }

}

Now a driver procedure is defined which uses our previously defined routine createConfigurationView() to create a view, and then starts processing:

/** Method to browse the part rooted at sRootPath

    of the configuration that the Provider provides.

    All nodes will be processed by the IConfigurationProcessor passed.

*/

public void browseConfiguration(String sRootPath, IConfigurationProcessor aProcessor)

        throws com.sun.star.uno.Exception {

    // create the root element

    XInterface xViewRoot = (XInterface)createConfigurationView(sRootPath);

    // now do the processing

    browseElementRecursively(xViewRoot, aProcessor);

    // we are done with the view - dispose it

    //   This assumes that the processor

    //   does not keep a reference to the elements in processStructuralElement

    ((XComponent) UnoRuntime.queryInterface(XComponent.class,xViewRoot)).dispose();

    xViewRoot = null;

}

Finally, as an example of how to put the code to use, the following is code to print the currently registered file filters:

/** Method to browse the filter configuration.

    Information about installed filters will be printed.

*/

public void printRegisteredFilters() throws com.sun.star.uno.Exception {

    final String sProviderService = "com.sun.star.configuration.ConfigurationProvider";

    final String sFilterKey = "/org.openoffice.Office.TypeDetection/Filters";

   // browse the configuration, dumping filter information

    browseConfiguration( sFilterKey,

       new IConfigurationProcessor () {  // anonymous implementation of our custom interface

           // prints Path and Value of properties

           public void processValueElement(String sPath_, Object aValue_) {

               System.out.println("\tValue: " + sPath_ + " = " + aValue_);

           }

           // prints the Filter entries

           public void processStructuralElement( String sPath_, XInterface xElement_) {

               // get template information, to detect instances of the 'Filter' template

               XTemplateInstance xInstance =

                   ( XTemplateInstance )UnoRuntime.queryInterface( XTemplateInstance .class,xElement_);

               // only select the Filter entries

               if (xInstance != null && xInstance.getTemplateName().endsWith("Filter")) {

                    XNamed xNamed = (XNamed)UnoRuntime.queryInterface(XNamed.class,xElement_);

                    System.out.println("Filter " + xNamed.getName() + " (" + sPath_ + ")");

               }

           }

       } );

}

For access to sub-nodes, a com.sun.star.configuration.ConfigurationAccess supports container interfaces com.sun.star.container.XNameAccess and com.sun.star.container.XChild. These interfaces access the immediate child nodes in the hierarchy , as well as com.sun.star.container.XHierarchicalNameAccess for direct access to items that are nested deeply.

These interfaces are uniformly supported by all structural configuration items. Therefore, they are utilized by code that browses a sub-tree of the configuration in a generic manner.

Parts of the hierarchy where the structure is known statically can also be viewed as representing a complex object composed of properties, that are composed of sub-properties themselves. This model is supported by the interface com.sun.star.beans.XPropertySet for child access and com.sun.star.beans.XHierarchicalPropertySet for access to deeply nested properties within such parts of the hierarchy. Due to the static nature of property sets, this model does not carry over to set nodes that are dynamic in nature and do not support the associated interfaces.

For effective access to multiple properties, the corresponding com.sun.star.beans.XMultiPropertySet and com.sun.star.beans.XMultiHierarchicalPropertySet interfaces are supported.


In a read-only view, all properties are marked as com.sun.star.beans.PropertyAttribute:READONLY in com.sun.star.beans.XPropertySetInfo. Attempts to use com.sun.star.beans.XPropertySet:setPropertyValue() to change the value of a property fail accordingly.

Typically, these interfaces are used to access a known set of preferences. The following example reads grid option settings from the OpenOffice.org Calc configuration into this structure:

class GridOptions

{

    public boolean visible;

    public int resolution_x;

    public int resolution_y;

    public int subdivision_x;

    public int subdivision_y;

};

These data may be read by a procedure such as the following. It demonstrates different approaches to read data:

// This method reads information about grid settings

protected GridOptions readGridConfiguration() throws com.sun.star.uno.Exception {

    // The path to the root element

    final String cGridOptionsPath = "/org.openoffice.Office.Calc/Grid";

    // create the view

    Object xViewRoot = createConfigurationView(cGridOptionsPath);

    // the result structure

    GridOptions options = new GridOptions();

    // accessing a single nested value

    // the item /org.openoffice.Office.Calc/Grid/Option/VisibleGrid is a boolean data item

    XHierarchicalPropertySet xProperties =

        (XHierarchicalPropertySet)UnoRuntime.queryInterface(XHierarchicalPropertySet.class, xViewRoot);

    Object aVisible = xProperties.getHierarchicalPropertyValue("Option/VisibleGrid");

    options.visible = ((Boolean) aVisible).booleanValue();

    // accessing a nested object and its subproperties

    // the item /org.openoffice.Office.Calc/Grid/Subdivision has sub-properties XAxis and YAxis

    Object xSubdivision = xProperties.getHierarchicalPropertyValue("Subdivision");

    XMultiPropertySet xSubdivProperties = (XMultiPropertySet)UnoRuntime.queryInterface(

        XMultiPropertySet.class, xSubdivision);

    // String array containing property names of sub-properties

    String[] aElementNames = new String[2];

    aElementNames[0] = "XAxis";

    aElementNames[1] = "YAxis";

    // getPropertyVAlues() returns an array of any objects according to the input array aElementNames

    Object[] aElementValues = xSubdivProperties.getPropertyValues(aElementNames);

    options.subdivision_x = ((Integer) aElementValues[0]).intValue();

    options.subdivision_y = ((Integer) aElementValues[1]).intValue();

    // accessing deeply nested subproperties

    // the item /org.openoffice.Office.Calc/Grid/Resolution has sub-properties

    // XAxis/Metric and YAxis/Metric

    Object xResolution = xProperties.getHierarchicalPropertyValue("Resolution");

    XMultiHierarchicalPropertySet xResolutionProperties = (XMultiHierarchicalPropertySet)

        UnoRuntime.queryInterface(XMultiHierarchicalPropertySet.class, xResolution);

    aElementNames[0] = "XAxis/Metric";

    aElementNames[1] = "YAxis/Metric";

    aElementValues = xResolutionProperties.getHierarchicalPropertyValues(aElementNames);

    options.resolution_x = ((Integer) aElementValues[0]).intValue();

    options.resolution_y = ((Integer) aElementValues[1]).intValue();

    // all options have been retrieved - clean up and return

    // we are done with the view - dispose it

    ((XComponent)UnoRuntime.queryInterface(XComponent.class, xViewRoot)).dispose();

    return options;

}

A com.sun.star.configuration.ConfigurationAccess also supports the interfaces com.sun.star.container.XNamed, com.sun.star.container.XHierarchicalName and com.sun.star.beans.XPropertySetInfo to retrieve information about the node, as well as interface com.sun.star.container.XChild to get the parent within the hierarchy. To monitor changes to specific items, register listeners at the interfaces com.sun.star.container.XContainer and com.sun.star.beans.XPropertySet.

The exact set of interfaces supported depends on the role of the node in the hierarchy. For example, a set node does not support com.sun.star.beans.XPropertySet and related interfaces, but it supports com.sun.star.configuration.XTemplateContainer to get information about the template that specifies the schema of elements. The root object of a configuration view does not support com.sun.star.container.XChild, but it supports com.sun.star.util.XChangesNotifier to monitor all changes in the whole view.

15.4.2    Updating Configuration Data

A com.sun.star.configuration.ConfigurationUpdateAccess provides operations for updating configuration data, by extendingthe interfaces supported by a com.sun.star.configuration.ConfigurationAccess.


Illustration 1.4: ConfigurationUpdateAccess services

For com.sun.star.beans.XPropertySet and related interfaces, the semantics are extended to set property values. Support for container interfaces is extended to set properties in group nodes, and insert or remove elements in set nodes. Thus, a com.sun.star.configuration.GroupUpdate supports interface com.sun.star.container.XNameReplace and a com.sun.star.configuration.SetUpdate supports com.sun.star.container.XNameContainer. Only complete trees having the appropriate structure are inserted for sets whose elements are complete structures as described by a template,. To support this, the set object is used as a factory that can create structures of the appropriate type. For this purpose, the set supports com.sun.star.lang.XSingleServiceFactory.

Updates done through a configuration view are only visible within that view, providing transactional isolation. When a set of updates is ready, it must be committed explicitly to become visible beyond this view. All pending updates are then sent to the configuration provider in one batch. This batch update behavior is controlled through interfacecom.sun.star.util.XChangesBatch that is implemented by the root element of an updatable configuration view.


When a set of changes is committed to the provider it becomes visible to other views obtained from the same provider as an atomic and consistent set of changes. Thus, in the local scope of a single com.sun.star.configuration.ConfigurationProvider a high degree of transactional behavior is achieved.

The configuration management component does not guarantee true transactional behavior. Committing the changes to the com.sun.star.configuration.ConfigurationProvider does not ensure persistence or durability of the changes. When the provider writes back the changes to the persistent data store, they become durable. Generally, the com.sun.star.configuration.ConfigurationProvider may cache and combine requests, so that updates are propagated to the data store at a later time.

If several sets of changes are combined before being saved, isolation and consistency may be weakened in case of failure. As long as the backend does not fully support transactions, only parts of an update request might be stored successfully, thus violating atomicity and consistency.

If failures occur while writing configuration data into the backend data store, the com.sun.star.configuration.ConfigurationProvider resynchronizes with the data stored in the backend. The listeners are notified of any differences as if they had been stored through another view. If an application has more stringent error handling needs, the caching behavior can be adjusted by providing arguments when creating the view.

In summary, , there are few overall guarantees regarding transactional integrity for the configuration database, but locally, the configuration behaves as if the support is in place. Depending on the backend capabilities, the com.sun.star.configuration.ConfigurationProvider tries to provide the best approximation to transactional integrity that can be achieved considering the capabilities of the backend without compromising performance.

The following example demonstrates how the configuration interfaces are used to feed a user-interface for preference changes. This shows the framework needed to update configuration values, and demonstrates how listeners are used with configuration views. This example concentrates on properties in group nodes with a fixed structure. It uses the same OpenOffice.org Calc grid settings as the previous example. It assumes that there is a class GridOptionsEditor that drives a dialog to display and edit the configuration data:

// This method simulates editing configuration data using a GridEditor dialog class  

public void editGridOptions() throws com.sun.star.uno.Exception {

    // The path to the root element

    final String cGridOptionsPath = "/org.openoffice.Office.Calc/Grid";

    // create a synchronous view for better error handling (lazywrite = false)

    Object xViewRoot = createUpdatableView(cGridOptionsPath, false);

    // the 'editor'

    GridOptionsEditor dialog = new GridOptionsEditor();

    // set up the initial values and register listeners

    // get a data access interface, to supply the view with a model

    XMultiHierarchicalPropertySet xProperties = (XMultiHierarchicalPropertySet)

        UnoRuntime.queryInterface(XMultiHierarchicalPropertySet.class, xViewRoot);

    dialog.setModel(xProperties);

    // get a listener object (probably an adapter) that notifies

    // the dialog of external changes to its model

    XChangesListener xListener = dialog.createChangesListener( );

    XChangesNotifier xNotifier =

        (XChangesNotifier)UnoRuntime.queryInterface(XChangesNotifier.class, xViewRoot);

    xNotifier.addChangesListener(xListener);

    if (dialog.execute() == GridOptionsEditor.SAVE_SETTINGS) {

        // changes have been applied to the view here

        XChangesBatch xUpdateControl =

            (XChangesBatch) UnoRuntime.queryInterface(XChangesBatch.class,xViewRoot);

   

        try {

           xUpdateControl.commitChanges();

        }

        catch (Exception e) {

            dialog.informUserOfError( e );

        }

    }

    // all changes have been handled - clean up and return

    // listener is done now

    xNotifier.removeChangesListener(xListener);

    // we are done with the view - dispose it

    ((XComponent)UnoRuntime.queryInterface(XComponent.class, xViewRoot)).dispose();

}

In this example, the dialog controller uses the com.sun.star.beans.XMultiHierarchicalPropertySet interface to read and change configuration values. If the grid options are changed and committed in another view, com.sun.star.util.XChangesListener:changesOccurred() is sent to the listener supplied by the dialog which can then update its display accordingly.

Note that a synchronous com.sun.star.configuration.ConfigurationUpdateAccess was created for this example (argument lazywrite==false). As the action here is driven by user interaction, synchronous committing is used to detect errors immediately.

Besides the values for the current user, there are also default values that are determined by merging the schema with any default layers. It is possible to retrieve the default values for individual properties, and to reset a property or a set node to their default states, thus backing out any changes done for the current user. For this purpose, group nodes support the interfaces com.sun.star.beans.XPropertyState and com.sun.star.beans.XMultiPropertyStates, offering operations to query if a property assumes its default state or the default value, and to reset an updatable property to its default state. The com.sun.star.beans.PropertyAttribute structss available through com.sun.star.beans.XPropertySetInfo:getProperty() are used to determine if a particular item or node supports this operation.

Individual set elements can not be reset because set nodes do not support com.sun.star.beans.XPropertyState. Instead a com.sun.star.configuration.SetAccess supports com.sun.star.beans.XPropertyWithStatethat resets the set as a whole.

The following is an example code using this feature to reset the OpenOffice.org Calc grid settings used in the preceding examples to their default state:

/// This method resets the grid settings to their default values

protected void resetGridConfiguration() throws com.sun.star.uno.Exception {

    // The path to the root element

    final String cGridOptionsPath = "/org.openoffice.Office.Calc/Grid";

    // create the view

    Object xViewRoot = createUpdatableView(cGridOptionsPath);

    // ### resetting a single nested value ###

    XHierarchicalNameAccess xHierarchicalAccess =

        (XHierarchicalNameAccess)UnoRuntime.queryInterface(XHierarchicalNameAccess.class, xViewRoot);

    // get using absolute name

    Object xOptions = xHierarchicalAccess.getByHierarchicalName(cGridOptionsPath + "/Option");

    XPropertyState xOptionState =

        (XPropertyState)UnoRuntime.queryInterface(XPropertyState.class, xOptions);

    xOptionState.setPropertyToDefault("VisibleGrid");

    // ### resetting more deeply nested values ###

    Object xResolutionX = xHierarchicalAccess.getByHierarchicalName("Resolution/XAxis");

    Object xResolutionY = xHierarchicalAccess.getByHierarchicalName("Resolution/YAxis");

    XPropertyState xResolutionStateX =

        (XPropertyState)UnoRuntime.queryInterface(XPropertyState.class, xResolutionX);

    XPropertyState xResolutionStateY =

        (XPropertyState)UnoRuntime.queryInterface(XPropertyState.class, xResolutionY);

    xResolutionStateX.setPropertyToDefault("Metric");

    xResolutionStateY.setPropertyToDefault("Metric");

    // ### resetting multiple sibling values ###

    Object xSubdivision = xHierarchicalAccess.getByHierarchicalName("Subdivision");

    XMultiPropertyStates xSubdivisionStates =

        (XMultiPropertyStates)UnoRuntime.queryInterface(XMultiPropertyStates.class, xSubdivision);

    xSubdivisionStates.setAllPropertiesToDefault();

    // commit the changes

    XChangesBatch xUpdateControl =

        (XChangesBatch) UnoRuntime.queryInterface(XChangesBatch.class, xViewRoot);

    xUpdateControl.commitChanges();

    // we are done with the view - dispose it

    ((XComponent)UnoRuntime.queryInterface(XComponent.class, xViewRoot)).dispose();

}


Currently, group nodes do not support the attribute com.sun.star.beans.PropertyAttribute:MAYBEDEFAULT set in the com.sun.star.beans.Property structure available from com.sun.star.beans.XPropertySetInfo. Attempts to use com.sun.star.beans.XPropertyState:setPropertyToDefault to reset an entire group node fail.

Also, because the group nodes can not be reset, the com.sun.star.beans.XPropertyState:setPropertyToDefault or com.sun.star.beans.XMultiPropertyStates:setAllPropertiesToDefault cannot be used to reset all descendents of a node.

It is intended to lift this restriction in a future release. To avoid unexpected changes in behavior when this change is introduced, you should apply com.sun.star.beans.XPropertyState:setPropertyToDefault only to actual properties, such as value items, or set nodes. In particular, you should avoid com.sun.star.beans.XMultiPropertyStates:setAllPropertiesToDefault() on group nodes.

A more comprehensive example is provided that shows how set elements are created and added, and how it employs advanced techniques for reducing the amount of data that needs to be loaded.

This example uses the OpenOffice.org configuration module org.openoffice.Office.DataAccess. This component has a set item DataSources that contains group items described by the template DataSourceDescription. A data source description holds information about the settings required to connect to a data source.

The template org.openoffice.Office.DataAccess/DataSourceDescription has the following properties that describe the data source connection:

Name

Type

Comment

URL

String

Data source URL.

IsPasswordRequired

Boolean

Is a password needed to connect.

TableFilter

String []

Filters tables for display.

TableTypeFilter

String []

Filters tables for display.

User

String

User name to be used for connecting.

LoginTimeout

int

Default timeout for connection attempt.

SuppressVersionColumns

Boolean

Controls display of certain data.

DataSourceSettings

set node

Contains DataSourceSetting entriesthat contain driver-specific settings.

Bookmarks

set node

Contains Bookmark entries that link to related documents, for example, Forms.

It also contains the binary properties NumberFormatSettings and LayoutInformation that store information for layout and display of the data source contents.It also contains the set items Tables and Queries containing the layout information for the data access views.

The example shows a procedure that creates and saves basic settings for connecting to a new data source. It uses an asynchronous com.sun.star.configuration.ConfigurationUpdateAccess. Thus, when com.sun.star.util.XChangesBatch:commitChanges is called, the data becomes visible at the com.sun.star.configuration.ConfigurationProvider, but is only stored in the provider's cache. It is written to the data store at later when the cache is automatically flushed by the com.sun.star.configuration.ConfigurationProvider. As this is done in the background there is no exception when the write-back fails.


The recommended methodto configure a new data source is to use the com.sun.star.sdb.DatabaseContext service as described in 12.2.1 Database Access - Data Sources in OpenOffice.org API - DatabaseContext. This is a high-level service that ensures that all the settings required to establish a connection are properly set.

Among the parameters of the routine is the name of the data source that must be chosen to uniquely identify the data source from other parameters directly related to the above properties. There also is a parameter to pass a list of entries for the DataSourceSettings set.

The resulting routine is: (Config/ConfigExamples.java)

// This method stores a data source for given connection data

void storeDataSource(

        String sDataSourceName,

        String sDataSourceURL,

        String sUser,

        boolean bNeedsPassword,

        int nTimeout,

        com.sun.star.beans.NamedValue [] aDriverSettings,

        String [] aTableFilter ) throws com.sun.star.uno.Exception {

    // create the view and get the data source element in a

    // helper method createDataSourceDescription() (see below)

    Object xDataSource = createDataSourceDescription(getProvider(), sDataSourceName);

    // set the values

    XPropertySet xDataSourceProperties = (XPropertySet)UnoRuntime.queryInterface(

        XPropertySet.class, xDataSource);

    xDataSourceProperties.setPropertyValue("URL", sDataSourceURL);

    xDataSourceProperties.setPropertyValue("User", sUser);

    xDataSourceProperties.setPropertyValue("IsPasswordRequired", new Boolean(bNeedsPassword));

    xDataSourceProperties.setPropertyValue("LoginTimeout", new Integer(nTimeout));

    if (aTableFilter != null)

            xDataSourceProperties.setPropertyValue("TableFilter", aTableFilter);

    // ### store the driver-specific settings ###

    if (aDriverSettings != null) {

        Object xSettingsSet = xDataSourceProperties.getPropertyValue("DataSourceSettings");

        // helper for storing (see below)

        storeSettings( xSettingsSet, aDriverSettings);

    }

    // ### save the data and dispose the view ###

    // recover the view root (helper method)

    Object xViewRoot = getViewRoot(xDataSource);

    // commit the changes

    XChangesBatch xUpdateControl = (XChangesBatch) UnoRuntime.queryInterface(

        XChangesBatch.class, xViewRoot);

   

    xUpdateControl.commitChanges();

    // now clean up

    ((XComponent) UnoRuntime.queryInterface(XComponent.class, xViewRoot)).dispose();

}

Notice the function createDataSourceDescription in our example.It is called to get a DataSourceDescription instance to access a pre-existing item, or create and insert a new item using the passed name.

The function is optimized to reduce the view to as little data as necessary. To this end it employs the depth parameter when creating the view.


The "depth" parameter for optimization purposes is used here for demonstration purposes only. Use of the "depth" flag does not have a noticeable effect on performance with the current implementation of the OpenOffice.org configuration management components. Actually, there are few cases where the use of this parameter has any value.

This results in a view where descendents of the root are only included in the view up to the given nesting depth. In this case, where depth = 1, only the immediate children are loaded. If the requested item is found, the function gets a deeper view for only that item, otherwise it creates a new instance. In the latter case, the item returned is not the root of the view. (Config/ConfigExamples.java)

/** This method gets the DataSourceDescription for a data source.

    It either gets the existing entry or creates a new instance.

    The method attempts to keep the view used as small as possible. In particular there

    is no view created, that contains data for all data source that are registered.

*/

Object createDataSourceDescription(XMultiServiceFactory xProvider, String sDataSourceName)

        throws com.sun.star.uno.Exception {

    // The service name: Need an update access:

    final String cUpdatableView = "com.sun.star.configuration.ConfigurationUpdateAccess";

     // The path to the DataSources set node

    final String cDataSourcesPath = "/org.openoffice.Office.DataAccess/DataSources";

   // creation arguments: nodepath  

    com.sun.star.beans.PropertyValue aPathArgument = new com.sun.star.beans.PropertyValue();

    aPathArgument.Name = "nodepath";

    aPathArgument.Value = cDataSourcesPath ;

    // creation arguments: commit mode  

    com.sun.star.beans.PropertyValue aModeArgument = new com.sun.star.beans.PropertyValue();

    aModeArgument.Name = "lazywrite";

    aModeArgument.Value = new Boolean(true);

    // creation arguments: depth  

    com.sun.star.beans.PropertyValue aDepthArgument = new com.sun.star.beans.PropertyValue();

    aDepthArgument.Name = "depth";

    aDepthArgument.Value = new Integer(1);

    Object[] aArguments = new Object[3];

    aArguments[0] = aPathArgument;

    aArguments[1] = aModeArgument;

    aArguments[2] = aDepthArgument;

    // create the view: asynchronously updatable, with depth 1

    Object xViewRoot =

        xProvider.createInstanceWithArguments(cUpdatableView, aArguments);

    XNameAccess xSetOfDataSources = (XNameAccess) UnoRuntime.queryInterface(

            XNameAccess.class,xViewRoot);

    Object xDataSourceDescriptor = null; // the result

    if (xSetOfDataSources .hasByName(sDataSourceName)) {

        // the element is there, but it is loaded only with depth zero !

        try {

            // the view should point to the element directly, so we need to extend the path

            XHierarchicalName xComposePath = (XHierarchicalName) UnoRuntime.queryInterface(

                XHierarchicalName.class, xSetOfDataSources );

       

            String sElementPath = xComposePath.composeHierarchicalName( sDataSourceName );

            // use the name of the element now

            aPathArgument.Value = sElementPath;

            // create another view now (without depth limit)

            Object[] aDeepArguments = new Object[2];

            aDeepArguments[0] = aPathArgument;

            aDeepArguments[1] = aModeArgument;

            // create the view: asynchronously updatable, with unlimited depth

            xDataSourceDescriptor  =

                  xProvider.createInstanceWithArguments(cUpdatableView, aDeepArguments);

            if ( xDataSourceDescriptor != null) // all went fine

            {

                // dispose the other view

                ((XComponent)UnoRuntime.queryInterface(XComponent.class, xViewRoot)).dispose();

                xViewRoot = null;

            }

        }

        catch (Exception e) {

            // something went wrong, we retry with a new element

            System.out.println("WARNING: An exception occurred while creating a view" +

                " for an existing data source: " + e);

            xDataSourceDescriptor  = null;

        }

    }

    // do we have a result element yet ?

    if (xDataSourceDescriptor == null) {

        // get the container

        XNameContainer xSetUpdate = (XNameContainer)UnoRuntime.queryInterface(

            XNameContainer.class, xViewRoot);

        // create a new detached set element (instance of DataSourceDescription)

        XSingleServiceFactory xElementFactory = (XSingleServiceFactory)UnoRuntime.queryInterface(

            XSingleServiceFactory.class, xSetUpdate);

        // the new element is the result !

        xDataSourceDescriptor  = xElementFactory.createInstance();

        // insert it - this also names the element

        xSetUpdate.insertByName( sDataSourceName ,  xDataSourceDescriptor  );

    }

    return xDataSourceDescriptor ;

}

A method is required to recover the view root from an element object, because it is unknown if the item is the root of the view or a descendant : (Config/ConfigExamples.java)

// This method get the view root node given an interface to any node in the view

public static Object getViewRoot(Object xElement) {

    Object xResult = xElement;

 

    // set the result to its parent until that would be null  

    Object xParent;

    do {

        XChild xParentAccess =

            (XChild) UnoRuntime.queryInterface(XChild.class,xResult);

        if (xParentAccess != null)

            xParent = xParentAccess.getParent();

        else

            xParent = null;

        if (xParent != null)

            xResult = xParent;

     }

     while (xParent != null);

     return xResult;

}

Another function used is storeDataSource is storeSettings to store an array of com.sun.star.beans.NamedValues in a set of DataSourceSetting items. A DataSourceSetting contains a single property named Value tht is set to any of the basic types supported for configuration values. This example demonstrates the two steps required to add a new item to a set node: (Config/ConfigExamples.java)

/// this method stores a number of settings in a set node containing DataSourceSetting objects

void storeSettings(Object xSettingsSet, com.sun.star.beans.NamedValue [] aSettings)

        throws com.sun.star.uno.Exception {

    if (aSettings == null)

        return;

    // get the settings set as a container

    XNameContainer xSettingsContainer =

        (XNameContainer) UnoRuntime.queryInterface( XNameContainer.class, xSettingsSet);

    // and get a factory interface for creating the entries

    XSingleServiceFactory xSettingsFactory =

        (XSingleServiceFactory) UnoRuntime.queryInterface(XSingleServiceFactory.class, xSettingsSet);

    // now insert the individual settings

    for (int i = 0; i < aSettings.length; ++i) {

        // create a DataSourceSetting object

        XPropertySet xSetting = (XPropertySet)

            UnoRuntime.queryInterface(XPropertySet.class, xSettingsFactory.createInstance());

        // can set the value before inserting

        xSetting.setPropertyValue("Value", aSettings[i].Value);

        // and now insert or replace as appropriate

        if (xSettingsContainer.hasByName(aSettings[i].Name))

            xSettingsContainer.replaceByName(aSettings[i].Name, xSetting);

        else

            xSettingsContainer.insertByName(aSettings[i].Name, xSetting);

    }

}

Besides adding a freshly created instance of a template, a set item can be removed from a set and added to any other set supporting the same template for its elements, provided both sets are part of the same view. You cannot move a set item between views, as this contradicts the transactional isolation of views. The set item you removed in one view will still be in its old place in the other. If a set item is moved between sets in one view and the changes are committed, the change appears in another overlapping view as removal of the original item and insertion of a new element in the target location, not as relocation of an identical element.


The methods com.sun.star.container.XNamed:setName() and com.sun.star.container.XChild:setParent() are supported by a com.sun.star.configuration.ConfigurationUpdateAccess only if it is a com.sun.star.configuration.SetElement. They offer another way to move an item within a set or from one set to another set.

In the current release of OpenOffice.org, these methods are not supported correctly. You can achieve the same effect by using a sequence of remove item - insert item.

To rename an item: (Config/ConfigExamples.java)

/// Does the same as xNamedItem.setName(sNewName) should do

void renameSetItem(XNamed xNamedItem, String sNewName) throws com.sun.star.uno.Exception {

    XChild xChildItem = (XChild)

        UnoRuntime.queryInterface(XChild.class, xNamedItem);

    XNameContainer xParentSet = (XNameContainer)

        UnoRuntime.queryInterface(XNameContainer.class, xChildItem.getParent());

    String sOldName = xNamedItem.getName();

    // now rename the item

    xParentSet.removeByName(sOldName);

    xParentSet.insertByName(sNewName,xNamedItem);

}    

To move an item to a different parent: (Config/ConfigExamples.java)

/// Does the same as xChildItem.setParent( xNewParent ) should do

void moveSetItem(XChild xChildItem, XNameContainer xNewParent) throws com.sun.star.uno.Exception {

    XNamed xNamedItem = (XNamed)

        UnoRuntime.queryInterface(XNamed.class, xChildItem);

    XNameContainer xOldParent = (XNameContainer)

        UnoRuntime.queryInterface(XNameContainer.class, xChildItem.getParent());

    String sItemName = xNamedItem.getName();

    // now rename the item

    xOldParent.removeByName(sItemName);

    xNewParent.insertByName(sItemName,xChildItem);

}

15.5    Customizing Configuration Data

The configuration management API is a data manipulation API. There is no support for data definition functionality. You cannot programmatically inspect, modify or create a configuration schema.

This release does not support adding configuration data for your own components by createing and installing a new configuration data file into a backend manually. The file format used for the current standard backend is not documented, and there is no documentation about the internal organization of the standard file-based backend and proper deployment of selfmade configuration data files. A migration to new, well-documented file formats that are still XML-based and to a documented organization of the data files in the standard backend are being prepared. This feature will become available in a future version of OpenOffice.org. For more information, visit http://util.openoffice.org.

15.6    Adding a Backend Data Store

At present, the code to select and access a particular data store is hardcoded into the configuration management component. We are working on providing a UNO-based interface to enable different data stores in a flexible manner. This feature will become available in a future version of OpenOffice.org. For more information, visit http://util.openoffice.org.

[ Previous document | Content Table | Next document ]