Updating Configuration Data

From Apache OpenOffice Wiki
Jump to: navigation, search



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

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 interface com.sun.star.util.XChangesBatch that is implemented by the root element of an updatable configuration view.

Documentation caution.png 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, <idlml>com.sun.star.util.XChangesListener:changesOccurred</idlml>() 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.Property structs available through <idlml>com.sun.star.beans.XPropertySetInfo:getPropertyByName</idlml>() or <idlml>com.sun.star.beans.XPropertySetInfo:getProperties</idlml>() 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();
  }

Template:Documentation/Note

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 time-out for connection attempt.
SuppressVersionColumns Boolean Controls display of certain data.
DataSourceSettings set node Contains DataSourceSetting entries that 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 <idlml>com.sun.star.util.XChangesBatch:commitChanges</idlml> 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.

Documentation caution.png The recommended method to configure a new data source is to use the com.sun.star.sdb.DatabaseContext service as described in 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:

  // 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.

Template:Documentation/Tip

This results in a view where descendants 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.

  /** 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 :

  // 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 that 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:

  /// 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.

Template:Documentation/Tip

Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages