LibreOffice Developer's Guide: Chapter 14 - Universal Content Broker

From The Document Foundation Wiki
Jump to navigation Jump to search

Capabilities

The Universal Content Broker (UCB) is a key part of the LibreOffice architecture. In general, the UCB provides a standard interface for generalized access to different data sources and functions for querying, modifying, and creating data contents. The LibreOffice document types are all handled by the UCB. In addition, it is used for help files, directory trees and resource links.

The advantage of delegating resource access to the UCB is, that document, folder and link handling can always be the same from the developer's perspective.It does not matter if you are storing in a file system, on an FTPWebDAV server, or in a document management system.

However, the UCB does not have to be used directly if you want to load and save LibreOffice documents.The <idl>com.sun.star.frame.Desktop</idl> service provides the necessary functions, hiding the comparably low-level UCB calls. See Handling Documents. The UCB allows you to administer files in a directory tree or read your own document stream, regardless of where the directory tree or the stream is located.

Architecture

Conceptually, the UCB can be pictured as an object system that consists of a core and a set of Universal Content Providers (UCPs). The UCPs are designed to mask the differences between access protocols, enabling developers to focus on the essentials of integrating resources through the UCB interface, instead of the complexities of an underlying protocol. To this end, each UCP implements an interface that facilitates access to a particular data source through a Uniform Resource Identifier (URI). When a client requests a particular resource, it addresses the UCB that calls a qualified UCP, based on the URI that is associated with the content.

As a rule, all data content is encapsulated in content objects. Each content object implements a standard set of interfaces, that includes functions for querying the content type and a select set of commands that can be run on the respective content, such as "open", "delete", and "move".

Note pin.svg

Note:
Whenever we refer to UCB commands, we put them in double quotes as in "getPropertyValues" to make a distinction between UCB commands and methods in general, which are written as getPropertyValues(). UCB commands are explained in the section Executing Content Commands below.

Each content object also has a set of attributes that can be read and set by an application, that include the title, the media type (MIME type), and different flags. The UCB API defines a set of standard commands and properties. There is a set of mandatory properties and commands that must be supported by any content implementation, as well as optional commands and properties with predefined semantics. The illustration below shows the relationship between the UCB, UCPs and UCB content objects.

The relationship between UCB, UCPs and UCB content objects

When a client requests a particular content, it addresses the UCB and passes on the corresponding URI. The UCB analyzes the URI and then calls the corresponding UCP which creates an object for the requested resource.

For example, when an application requests a particular document, the URI of the document is passed to the Universal Content Broker. The UCB analyzes the URI and delegates it to the appropriate UCP. The UCP creates a content object for the requested resource and returns it to the UCB, which returns it to the application. The application now opens the content object or query, or set property values by executing the appropriate command.

Services and Interfaces

Each UCB content implements the service <idl>com.sun.star.ucb.Content</idl>. The UCB content service interfaces include:

  • <idl>com.sun.star.ucb.XContent</idl>
  • <idl>com.sun.star.beans.XPropertyContainer</idl>
  • <idl>com.sun.star.container.XChild</idl> (optional)
  • <idl>com.sun.star.ucb.XCommandProcessor</idl>
  • <idl>com.sun.star.ucb.XCommandProcessor2</idl> (optional)
  • <idl>com.sun.star.ucb.XContentCreator</idl> (optional)

The interface <idl>com.sun.star.ucb.XContent</idl> provides access to a content's type and identifier. The <idl>com.sun.star.ucb.XCommandProcessor</idl> executes commands at the content object, such as opening a content that provides access to the content's data stream or its children, and setting and getting property values. The interface <idl>com.sun.star.beans.XPropertyContainer</idl> adds new properties to a content or removes properties that were previously added using this interface. The properties added are always made persistent.

Warning.svg

Warning:
If you change the set of properties by adding or removing properties, the cache of scripting languages, such as LibreOffice Basic might not reflect these changes. Thus, use the get/set methods to access the properties in scripting languages rather than relying on their automatic recognition of properties.


The <idl>com.sun.star.ucb.XContentCreator</idl> interface is for creating new resources, such as a new folder in the local file system. Not all content implementation can create new resources, therefore this interface is optional. The optional interface <idl>com.sun.star.container.XChild</idl> provides access to the content object's parent content object. Not all data sources represented by content implementations are organized hierarchically, therefore a parent cannot always be specified.

Note pin.svg

Note:
The interface <idl>com.sun.star.ucb.XCommandProcessor2</idl> is the improved version of <idl>com.sun.star.ucb.XCommandProcessor</idl>. It has been introduced to release command identifiers retrieved through createCommandIdentifier() at the XCommandProcessor interface. To avoid resource leaks, use XCommandProcessor2.

Some content commands defined by the UCB API are listed in the following table:

Selected Command Names for <idl>com.sun.star.ucb.XCommandProcessor</idl>
"getCommandInfo" Obtains an interface that queries information on commands supported by a content.
"getPropertySetInfo" Obtains an interface that queries information on properties supported by a content.
"getPropertyValues" Obtains property values from the content.
"setPropertyValues" Sets property values of the content.
"open" Gives access to the data stream of a document or to the children of a folder.
"delete" Destroys a resource.
"insert" Commits newly-created resources. Writes new data stream of existing document resources.
"transfer" Copies or moves a content object.

Some interesting content properties defined by the UCB API:

Selected Properties of UCB Contents
ContentType Contains a unique(!), read-only type string for the content, for example, "application/vnd.sun.star.hierarchy-link". This is not the Media-Type!
IsFolder Indicates whether a content can contain other contents.
IsDocument Indicates whether a content is a document.
Title Contains the title of an object, for example, the name of a file.
DateCreated Contains the date and time the object was created.
DateModified Contains the date and time the object was last modified.
MediaType Contains the media type (MIME type) of a content.
Size Contains the size, usually in bytes, of an object.

Every UCP implements the service <idl>com.sun.star.ucb.ContentProvider</idl>. The UCP core interface is <idl>com.sun.star.ucb.XContentProvider</idl>. This interface facilitates the creation of content objects based on a given content identifier.

A UCB implements the service <idl>com.sun.star.ucb.UniversalContentBroker</idl>. The UCB core interfaces are <idl>com.sun.star.ucb.XContentProvider</idl> and <idl>com.sun.star.ucb.XContentProviderManager</idl>. The <idl>com.sun.star.ucb.XContentProvider</idl> interface implementation delegates requests to create content objects to the content provider registered for the supplied content identifier. The <idl>com.sun.star.ucb.XContentProviderManager</idl> interface is used to query the UCPs registered with a given UCB, and to register and remove UCPs.

Note pin.svg

Note:
A specification for the implementation for each of the UCPs, including URL schemes, content types, supported commands and properties is located in Universal Content Providers.

Content Providers

The current implementation of the Universal Content Broker in a LibreOffice installation supplies UCPs for the following data sources:

Data source Description URL Schema Service name
FILE Provides access to the file system "file" com.sun.star.ucb.FileContentProvider
WebDAV and HTTP Provides access to web-based file systems and includes HTTP "vnd.sun.star.webdav" or "http" com.sun.star.ucb.WebDAVContentProvider
FTP Provides access to file transfer protocol servers "ftp" com.sun.star.ucb.fpx.ContentProvider
Hierarchy Virtual hierarchy of folders and links "vnd.sun.star.hier" com.sun.star.ucb.HierarchyContentProvider
ZIP and JAR files Packaged files "vnd.sun.star.pkg" com.sun.star.ucb.PackageContentProvider
Help files LibreOffice help system "vnd.sun.star.help" com.sun.star.help.XMLHelp
Extension package content provides access to the content of an installed extension, specified by the extension's identifier. "vnd.sun.star.extension://extension-id/relative-path-within-extension-package" com.sun.star.ucb.ExtensionContentProvider

Appendix C Universal Content Providers describes all the above content providers in more detail. The reference documentation for the commands and other features of these UCPs are located in the SDK or the ucb project on ucb.openoffice.org. Additionally, the ucb project offers information about other UCPs for LibreOffice, for example, a UCP for document management systems.

Using the UCB API

This section explains how to use the API of the Universal Content Broker.

Instantiating the UCB

The following steps have to be performed before a process can use the UCB:

  • Create and set the UNO service manager.
  • Create an instance of the UNO service <idl>com.sun.star.ucb.UniversalContentBroker</idl>, passing the keys identifying a predefined UCB configuration to createInstanceWithArguments().

There are several predefined UCB configurations. Each configuration contains data that describes a set of UCPs. All UCPs contained in a configuration are registered at the UCB that is created using this configuration. A UCB configuration is identified by two keys that are strings. The standard configuration is "Local" and "Office", which generally allows access to all UCPs available in a local installation.

import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.uno.Exception;
import com.sun.star.uno.XInterface;

boolean initUCB() {

    /////////////////////////////////////////////////////////////////////
    // Obtain Process Service Manager.
    /////////////////////////////////////////////////////////////////////

    XMultiServiceFactory xServiceFactory = ...

    /////////////////////////////////////////////////////////////////////
    // Create UCB. This needs to be done only once per process.
    /////////////////////////////////////////////////////////////////////

    XInterface xUCB;
    try {
        // Supply configuration to use for this UCB instance...
        String[] keys = new String[2];
        keys[ 0 ] = "Local";
        keys[ 1 ] = "Office";

        xUCB = xServiceFactory.createInstanceWithArguments(
            "com.sun.star.ucb.UniversalContentBroker", keys );
    }
    catch (com.sun.star.uno.Exception e) {
    }

    if (xUCB == null)
        return false;

    return true;
}

For information about other configurations, refer to Configuration.

Accessing a UCB Content

Each UCB content can be identified using a URL that points to a folder or a document content in the data source you want to work with. To create a content object for a given URL:

  1. Obtain access to the UCB.
  2. Let the UCB create a content identifier object for the requested URL using createContentIdentifier() at the <idl>com.sun.star.ucb.XContentIdentifierFactory</idl> of the UCB.
  3. Let the UCB create a content object for the content identifier using queryContent() at the <idl>com.sun.star.ucb.XContentProvider</idl> interface of the UCB.

The UCB selects a UCP according to the URL contained in the identifier object and dispatches the queryContent() call to it. The UCP creates the content implementation object and returns it to the UCB, which passes it on to the caller.

Creating a UCB content from a given URL:

import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.Xinterface;
import com.sun.star.ucb.*;

{
    String aURL = ...

    /////////////////////////////////////////////////////////////////////
    // Obtain access to UCB...
    /////////////////////////////////////////////////////////////////////

    XInterface xUCB = ...

    // Obtain required UCB interfaces XContentIdentifierFactory and XContentProvider
    XContentIdentifierFactory xIdFactory = (XContentIdentifierFactory)UnoRuntime.queryInterface(
        XContentIdentifierFactory.class, xUCB);
    XContentProvider xProvider = (XContentProvider)UnoRuntime.queryInterface(
        XContentProvider.class, xUCB);

    /////////////////////////////////////////////////////////////////////
    // Obtain content object from UCB...
    /////////////////////////////////////////////////////////////////////

    // Create identifier object for given URL.
    XContentIdentifier xId = xIdFactory.createContentIdentifier(aURL);

    XContent xContent = xProvider.queryContent(xId);
}

Executing Content Commands

Each UCB content is able to execute commands. When the content object is created, commands are executed using its <idl>com.sun.star.ucb.XCommandProcessor</idl> interface. The <idlm>com.sun.star.ucb.XCommandProcessor:execute</idlm>() method at this interface expects a <idl>com.sun.star.ucb.Command</idl>, which is a struct containing the command name, command arguments and a handle:

Members of struct <idl>com.sun.star.ucb.Command</idl>
<idlm>com.sun.star.ucb.Command:Name</idlm> string, contains the name of the command
<idlm>com.sun.star.ucb.Command:Handle</idlm> long, contains an implementation-specific handle for the command
<idlm>com.sun.star.ucb.Command:Argument</idlm> any, contains the argument of the command

Refer to Appendix C Universal Content Providers for a complete list of predefined commands, the description of the UNO service <idl>com.sun.star.ucb.Content</idl> and the UCP reference that comes with the SDK. For the latest information, visit ucb.openoffice.org.

Note pin.svg

Note:
Whenever we refer to UCB commands, we put them in double quotes as in "getPropertyValues" to make a distinction between UCB commands and methods in general that are written getPropertyValues().

If executing a command cannot proceed because of an error condition, the following occurs. If the execute call was supplied with a <idl>com.sun.star.ucb.XCommandEnvironment</idl> that contains a <idl>com.sun.star.task.XInteractionHandler</idl>, this interaction handler is used to resolve the problem. If no interaction handler is supplied by passing null to the execute() method, or it cannot resolve the problem, an exception describing the error condition is thrown.

The following method executeCommand() executes a command at a UCB content:

import com.sun.star.uno.UnoRuntime;
import com.sun.star.ucb.*;

Object executeCommand(XContent xContent, String aCommandName, Object aArgument)
    throws com.sun.star.ucb.CommandAbortedException, com.sun.star.uno.Exception {

    /////////////////////////////////////////////////////////////////////
    // Obtain command processor interface from given content.
    /////////////////////////////////////////////////////////////////////

    XCommandProcessor xCmdProcessor = (XCommandProcessor)UnoRuntime.queryInterface(
        XCommandProcessor.class, xContent);

    /////////////////////////////////////////////////////////////////////
    // Assemble command to execute.
    /////////////////////////////////////////////////////////////////////

    Command aCommand = new Command();
    aCommand.Name = aCommandName;
    aCommand.Handle = -1; // not available
    aCommand.Argument = aArgument;

    // Note: throws CommandAbortedException and Exception since
    // we pass null for the XCommandEnvironment parameter
    return xCmdProcessor.execute(aCommand, 0, null);
}

Note pin.svg

Note:
The method executeCommand() from the example above is used in the following examples whenever a command is to be executed at a UCB content.

Obtaining Content Properties

A UCB content maintains a set of properties. It supports the command "getPropertyValues", that obtains one or more property values from a content. This command takes a sequence of <idl>com.sun.star.beans.Property</idl> and returns an implementation of the interface <idl>com.sun.star.sdbc.XRow</idl> that is similar to a row of a JDBC resultset. To obtain property values from a UCB content:

  1. Define a sequence of properties you want to obtain the values for.
  2. Let the UCB content execute the command "getPropertyValues".
  3. Obtain the property values from the returned row object.

The following example demonstrates the use of content properties. Note that the method executeCommand() is used from the example above to execute the "getPropertyValues" command that takes a command name and creates a <idl>com.sun.star.ucb.Command</idl> struct from it:

import com.sun.star.ucb.*;
import com.sun.star.sdbc.XRow;
import com.sun.star.beans.Property;

{
    XContent xContent = ...

    /////////////////////////////////////////////////////////////////////
    // Obtain value of the string property Title and the boolean property
    // IsFolder from xContent...
    /////////////////////////////////////////////////////////////////////

    // Define property sequence.

    Property[] aProps = new Property[2];
    Property prop1 = new Property();
    prop1.Name = "Title";
    prop1.Handle = -1;// n/a
    aProps[0] = prop1;
    Property prop2 = new Property();
    prop2.Name = "IsFolder";
    prop2.Handle = -1;// n/a
    aProps[1] = prop2;

    XRow xValues;
    try {
        // Execute command "getPropertyValues"
        // using helper method executeCommand (see [CHAPTER:UCB.Using.Commands])
        xValues = executeCommand(xContent, "getPropertyValues", aProps);
    }
    catch (com.sun.star.ucb.CommandAbortedException e) {
        ... error ...
    }
    catch ( com.sun.star.uno.Exception e ) {
        ... error ...
    }

    // Extract values from row object. Note that the
    // first column is 1, not 0.

    // Title: Obtain value of column 1 as string.
    String aTitle = xValues.getString(1));
    if (aTitle.length() == 0 && xValues.wasNull())
        ... error ...

    // IsFolder: Obtain value of column 2 as boolean.
    boolean bFolder = xValues.getBoolean(2);
    if (!bFolder && xValues.wasNull())
        ... error ...
}

The returned row for the content above has two columns Title and IsFolder, and could contain the following data. The column values are retrieved using the getXXX methods of the <idl>com.sun.star.sdbc.XRow</idl> interface. The command "getPropertyValues" always returns a single row for contents.

Title IsFolder
"MyFolder" TRUE

Setting Content Properties

A UCB content maintains a set of properties. It supports the command "setPropertyValues", that is used to set one or more property values of a content. This command takes a sequence of <idl>com.sun.star.beans.PropertyValue</idl> and returns void. To set property values of a UCB content:

  • ::Define a sequence of property values you want to set.
  • ::Let the UCB content execute the command "setPropertyValues".

Note that the command is not aborted if one or more of the property values cannot be set, because the requested property is not supported by the content or because it is read-only. Currently, there is no other method to check if a property value was set successfully other than to obtain the property value after a set-operation. This may change when status information could be returned by the command "setPropertyValues".

Setting property values of a UCB content:

import com.sun.star.ucb.*;
import com.sun.star.beans.PropertyValue;

{
    XContent xContent = ...
    String aNewTitle = "NewTitle";

    /////////////////////////////////////////////////////////////////////
    // Set value of the string property Title...
    /////////////////////////////////////////////////////////////////////

    // Define property value sequence.

    PropertyValue[] aProps = new PropertyValue[ 1 ];
    PropertyValue aProp = new PropertyValue();
    aProp.Name = "Title";
    aProp.Handle = -1;// n/a
    aProp.Value = aNewTitle;
    aProps[0] = aProp;

    try {
        // Execute command "setPropertyValues".
        // using helper method executeCommand (see [CHAPTER:UCB.Using.Commands]).
        executeCommand(xContent, "setPropertyValues", aProps);
    }
    catch (com.sun.star.ucb.CommandAbortedException e) {
        ... error ...
    }
    catch (com.sun.star.uno.Exception e) {
        ... error ...
    }
}

Folders

Accessing the Children of a Folder

A UCB content that is a folder, that is, the value of the required property IsFolder is true, supports the command "open". This command takes an argument of type <idl>com.sun.star.ucb.OpenCommandArgument2</idl>. The value returned is an implementation of the service <idl>com.sun.star.ucb.DynamicResultSet</idl>. This DynamicResultSet holds the children of the folder and is a result set that can notify registered listeners about changes. To retrieve data from it, call getStaticResultSet() at its <idl>com.sun.star.ucb.XDynamicResultSet</idl> interface. The static result set is a <idl>com.sun.star.sdbc.XResultSet</idl> that can be seen as a table, where each row contains a child content of the folder. Use the appropriate methods of <idl>com.sun.star.sdbc.XResultSet</idl> to navigate through the rows:

boolean first()
boolean last()
boolean next()
boolean previous()
boolean absolute( [in] long row)
boolean relative( [in] long rows)
void afterLast()
void beforeFirst()
boolean isBeforeFirst()
boolean isAfterLast()
boolean isFirst()
boolean isLast()
long getRow()

The child contents are accessed by traveling to the appropriate row and using the interface <idl>com.sun.star.ucb.XContentAccess</idl>, which is implemented by the returned result set:

com::sun::star::ucb::XContent queryContent()
string queryContentIdentifierString()
com::sun::star::ucb::XContentIdentifier queryContentIdentifier()

You may supply a sequence of <idl>com.sun.star.beans.Property</idl> as part of the argument of the "open" command. In this case, the result set contains one column for each property value that is requested. The property values are accessed by traveling to the appropriate row and calling methods of the interface <idl>com.sun.star.sdbc.XRow</idl>. Refer to the documentation of <idl>com.sun.star.ucb.OpenCommandArgument2</idl> for more information about other parameters that can be passed to the "open" command.

To access the children of a UCB content:

  1. Fill the <idl>com.sun.star.ucb.OpenCommandArgument2</idl> structure according to your requirements.
  2. Let the UCB content execute the "open" command.
  3. Access the children and the requested property values using the returned dynamic result set.

Accessing the children of a UCB folder content:

import com.sun.star.uno.UnoRuntime;
import com.sun.star.ucb.*;
import com.sun.star.sdbc.XResultSet;
import com.sun.star.sdbc.XRow;

{
    XContent xContent = ...

    /////////////////////////////////////////////////////////////////////
    // Open a folder content, request property values for the string
    // property Title and the boolean property IsFolder...
    /////////////////////////////////////////////////////////////////////
    // Fill argument structure...

    OpenCommandArgument2 aArg = new OpenCommandArgument2();
    aArg.Mode = OpenMode.ALL;// FOLDER, DOCUMENTS -> simple filter
    aArg.Priority = 32768;// Ignored by most implementations

    // Fill info for the properties wanted.
    Property[] aProps = new Property[2];
    Property prop1 = new Property();
    prop1.Name = "Title";
    prop1.Handle = -1;// n/a
    aProps[0] = prop1;
    Property prop2 = new Property();
    prop2.Name = "IsFolder";
    prop2.Handle = -1;// n/a
    aProps[1] = prop2;

    aArg.Properties = aProps;

    XDynamicResultSet xSet;
    try {
        // Execute command "open".
        // using helper method executeCommand (see [CHAPTER:UCB.Using.Commands].
        xSet = executeCommand(xContent, "open", aArg);
    }
    catch (com.sun.star.ucb.CommandAbortedException e) {
        ... error ...
    }
    catch (com.sun.star.uno.Exception e) {
        ... error ...
    }

    XResultSet xResultSet = xSet.getStaticResultSet();

    /////////////////////////////////////////////////////////////////////
    // Iterate over children, access children and property values...
    /////////////////////////////////////////////////////////////////////

    try {
        // Move to begin.
        if (xResultSet.first()) {
            // obtain XContentAccess interface for child content access and XRow for properties
            XContentAccess xContentAccess = (XContentAccess)UnoRuntime.queryInterface(
            XContentAccess.class, xResultSet);
            XRow xRow = (XRow)UnoRuntime.queryInterface(XRow.class, xResultSet);
            do {
                // Obtain URL of child.
                String aId = xContentAccess.queryContentIdentifierString();

                // First column: Title (column numbers are 1-based!)
                String aTitle = xRow.getString(1);
                if (aTitle.length() == 0 && xRow.wasNull())
                    ... error ...

                // Second column: IsFolder
                boolean bFolder = xRow.getBoolean(2);
                if (!bFolder && xRow.wasNull())
                    ... error ...
            } while (xResultSet.next()) // next child
        }
    }
    catch (com.sun.star.ucb.ResultSetException e) {
        ... error ...
    }
}

Documents

Reading a Document Content

A UCB content that is a document, that is, the value of the required property IsDocument is true, supports the command "open". The command takes an argument of type <idl>com.sun.star.ucb.OpenCommandArgument2</idl>. Note that this command with the same argument type is also used to access the children of a folder. As seen in the examples, the argument's Mode member controls access to the children or the data stream, or both for contents that support both. If you are interested in the data stream, ignore the command's return value, which will presumably be a null value.

The caller must implement a data sink and supply this implementation as "open" command arguments to get access to the data stream of a document. These data sinks are called back by the implementation when the "open" command is executed. There are two different interfaces for data sinks to choose from, <idl>com.sun.star.io.XActiveDataSink</idl> and <idl>com.sun.star.io.XOutputStream</idl>.

  • XActiveDataSink: If this type of data sink is supplied, the caller of the command is active. It consists of the following methods:

void setInputStream( [in] com::sun::star::io::XInputStream aStream)
com::sun::star::io::XInputStream getInputStream()

The implementation of the command supplies an implementation of the interface <idl>com.sun.star.io.XInputStream</idl> to the given data sink using setInputStream() and return. Once the execute-call has returned, the caller accesses the input stream calling getInputStream() and read the data using that stream, through readBytes() or readSomeBytes().

  • XOutputStream: If this type of data sink is supplied, the caller of the command is passive. The data sink is called back through the following methods of XOutputStream:

void writeBytes( [in] sequence< byte > aData)
void closeOutput()
void flush()

The implementation of the command writes all data to the output stream calling writeBytes() and closes it through closeOutput() after all data was successfully written. Only then will the open command return.

The type to use depends on the logic of the client application. If the application is designed so that it passively processes the data supplied by an <idl>com.sun.star.io.XOutputStream</idl> using an output stream as sink is advantageous, because many content providers implement this case efficiently without buffering any data. If the application is designed so that it actively reads the data, use an <idl>com.sun.star.io.XActiveDataSink</idl>, then any necessary buffering takes place in the implementation of the open command.

The following example shows a possible implementation of an <idl>com.sun.star.io.XActiveDataSink</idl> and its usage with the "open" command:

import com.sun.star.io.XActiveDataSink;
import com.sun.star.io.XInputStream;

/////////////////////////////////////////////////////////////////////////
// XActiveDataSink interface implementation.
/////////////////////////////////////////////////////////////////////////

public class MyActiveDataSink implements XActiveDataSink {
    XInputStream m_aStream = null;

    public MyActiveDataSink() {
        super();
    }

    public void setInputStream(XInputStream aStream) {
        m_aStream = aStream;
    }

    public XInputStream getInputStream() {
        return m_aStream;
    }
};

Now this data sink implementation can be used with the "open" command. After opening the document content, getInputStream() returns the input stream containing the document data. The actual byte content is read using readSomeBytes() in the following fragment:

import com.sun.star.ucb.*;
import ...MyActiveDataSink;

{
    XContent xContent = ...

    /////////////////////////////////////////////////////////////////////
    // Read the document data stream of a document content using a
    // XActiveDataSink implementation as data sink....
    /////////////////////////////////////////////////////////////////////
    // Fill argument structure...

    OpenCommandArgument2 aArg = new OpenCommandArgument2;
    aArg.Mode = OpenMode.DOCUMENT;
    aArg.Priority = 32768;// Ignored by most implementations

    // Create data sink implementation object.
    XActiveDataSink xDataSink = new MyActiveDataSink;
    aArg.Sink = xDataSink;

    try {
        // Execute command "open". The implementation of the command will
        // supply an XInputStream implementation to the data sink,
        // using helper method executeCommand (see [[#Executing Content Commands|Executing Content Commands]])
        executeCommand(xContent, "open", aArg);
    }
    catch (com.sun.star.ucb.CommandAbortedException e) {
        ... error ...
    }
    catch (com.sun.star.uno.Exception e) {
        ... error ...
    }

    // Get input stream supplied by the open command implementation.
    XInputStream xData = xDataSink.getInputStream();
    if (xData == null)
        ... error ...

    /////////////////////////////////////////////////////////////////////
    // Read data from input stream...
    /////////////////////////////////////////////////////////////////////
    try {
        // Data buffer. Will be allocated by input stream implementation!
        byte[][] aBuffer = new byte[1][1];

        int nRead = xData.readSomeBytes(aBuffer, 65536);
        while (nRead > 0) {
            // Process data contained in buffer.
            ...

            nRead = xData.readSomeBytes(aBuffer, 65536);
        }

        // EOF.
    }
    catch (com.sun.star.io.NotConnectedException e) {
        ... error ...
    }
    catch (com.sun.star.io.BufferSizeExceededException e) {
        ... error ...
    }
    catch (com.sun.star.io.IOException e) {
        ... error ...
    }
}

Storing a Document Content

A UCB content that is a document, that is, the value of the required property IsDocument is true, supports the command "insert". This command is used to overwrite the document's data stream. The command requires an argument of type <idl>com.sun.star.ucb.InsertCommandArgument</idl> and returns void. The caller supplies the implementation of an <idl>com.sun.star.io.XInputStream</idl> with the command argument. This stream contains the data to be written. An additional flag indicating if an existing content and its data should be overwritten is supplied with the command argument. Implementations that are not able to detect if there are previous data may ignore this parameter and will always write the new data.

Setting or storing the content data stream of a UCB document content is shown below:

import com.sun.star.ucb.*;
import com.sun.star.io.XInputStream;

{
    XContent xContent = ...
    XInputStream xData = ... // The data to write.

    /////////////////////////////////////////////////////////////////////
    // Write the document data stream of a document content...
    /////////////////////////////////////////////////////////////////////

    // Fill argument structure...

    InsertCommandArgument aArg = new InsertCommandArgument();
    aArg.Data = xData;
    aArg.ReplaceExisting = true;

    try {
        // Execute command "insert".
        // using helper method executeCommand (see [CHAPTER:UCB.Using.Commands]).
        executeCommand(xContent, "insert", aArg);
    }
    catch (com.sun.star.ucb.CommandAbortedException e) {
        ... error ...
    }
    catch (com.sun.star.uno.Exception e) {
        ... error ...
    }
}

Managing Contents

You can create, delete, copy, move and link UCB content.

Creating

A UCB content that implements the interface <idl>com.sun.star.ucb.XContentCreator</idl> acts as a factory for new resources. For example, a file system folder can be a creator for other file system folders and files.

A new content object created by the <idl>com.sun.star.ucb.XContentCreator</idl> implementation can be considered as an empty hull for a content object of a special type. This new content object has to be filled with some property values to become fully functional. For example, a file system folder could require a name, represented by the property Title in the UCB. The interface <idl>com.sun.star.ucb.XContentCreator</idl> offers ways to determine what contents can be created and what properties need to be set. Information can be obtained on the general type, such as FOLDER, DOCUMENT, or LINK, of the objects. After the required property values are set, the creation process needs to be committed by using the command "insert". Note that this command is always executed by the new content, not by the content creator, because the creator is not necessarily the parent of the new content. The flag ReplaceExisting in the "insert" argument <idl>com.sun.star.ucb.InsertCommandArgument</idl> usually is false, because the caller does not want to destroy an already existing resource. The "insert" command implementation makes the new content persistent in the appropriate storage medium.

To create a new resource:

  1. Obtain the interface <idl>com.sun.star.ucb.XContentCreator</idl> from a suitable UCB content.
  2. Call createNewContent() at the content creator. Supply information on the type of content to create with the arguments. The argument expected is a <idl>com.sun.star.ucb.ContentInfo</idl> struct.
  3. Obtain and set the property values that are mandatory for the content just created.
  4. Let the new content execute the command "insert" to complete the creation process.

Creating a new resource:

import com.sun.star.uno.UnoRuntime;
import com.sun.star.ucb.*;
import com.sun.star.beans.PropertyValue;
import com.sun.star.io.XInputStream;

{
    XContent xContent = ...

    /////////////////////////////////////////////////////////////////////
    // Create a new file system file object...
    /////////////////////////////////////////////////////////////////////

    // Obtain content creator interface.
    XContentCreator xCreator = (XContentCreator)UnoRuntime.queryInterface(
        XContentCreator.class, xContent);

    // Note: The data for aInfo may have been obtained using
    // XContentCreator::queryCreatableContentsInfo().
    // A number of possible types is listed below

    ContentInfo aInfo = new ContentInfo();
    aInfo.Type = "application/vnd.sun.staroffice.fsys-file";
    aInfo.Attributes = 0;

    // Create new, empty content.
    XContent xNewContent = xCreator.createNewContent(aInfo);

    if (xNewContent == null)
        ... error ...

    /////////////////////////////////////////////////////////////////////
    // Set mandatory properties...
    /////////////////////////////////////////////////////////////////////

    // Obtain a name for the new file.
    String aFilename = ...

    // Define property value sequence.
    PropertyValue[] aProps = new PropertyValue[1];
    PropertyValue aProp = new PropertyValue;
    aProp.Name = "Title";
    aProp.Handle = -1; // n/a
    aProp.Value = aFilename;
    aProps[ 0 ] = aProp;
    try {
        // Execute command "setPropertyValues".
        // using helper method executeCommand (see [CHAPTER:UCB.Using.Commands])
        executeCommand(xNewContent, "setPropertyValues",aProps);
    }
    catch (com.sun.star.ucb.CommandAbortedException e) {
        ... error ...
    }
    catch (com.sun.star.uno.Exception e) {
        ... error ...
    }

    /////////////////////////////////////////////////////////////////////
    // Write the new file to disk...
    /////////////////////////////////////////////////////////////////////

    // Obtain document data for the new file.
    XInputStream xData = ...

    // Fill argument structure...
    InsertCommandArgument aArg = new InsertCommandArgument();
    aArg.Data = xData;
    aArg.ReplaceExisting = false;

    try {
        // Execute command "insert".
        executeCommand(xNewContent, "insert", aArg);
    }
    catch (com.sun.star.ucb.CommandAbortedException e) {
        ... error ...
    }
    catch (com.sun.star.uno.Exception e) {
        ... error ...
    }
}

Appendix C Universal Content Providers discusses the creation of contents for all available UCPs. The table below shows a number of <idl>com.sun.star.ucb.ContentInfo</idl> types for creatable contents. Additionally, you can ask the content creator for its creatable contents using <idlm>com.sun.star.ucb.XContentCreator:queryCreatableContentsInfo</idlm>(). The UCB reference in the SDK and on ucb.openoffice.org offers comprehensive information about creatable contents.

Data source Content Info Type Content Content Service that Creates the Contents
FILE "application/vnd.sun.staroffice.fsys-folder" folder com.sun.star.ucb.FileContent
"application/vnd.sun.staroffice.fsys-file" document
WebDAV and HTTP "application/vnd.sun.star.webdav-collection" folder com.sun.star.ucb.WebDAVFolderContent
"application/http-content" document
FTP "application/vnd.sun.staroffice.ftp-folder" folder com.sun.star.ucb.ChaosContent
"application/vnd.sun.staroffice.ftp-file" document
Hierarchy "application/vnd.sun.star.hier-folder" folder com.sun.star.ucb.HierarchyFolderContent
"application/vnd.sun.star.hier-link" Link
ZIP and JAR files "application/vnd.sun.star.pkg-folder" folder com.sun.star.ucb.PackageFolderContent
"application/vnd.sun.star.pkg-stream" document

Deleting

Executing the command "delete" on a UCB content destroys the resource it represents. This command takes a boolean parameter. If it is set to true, the resource is immediately, destroyed physically.

Warning.svg

Warning:
The command also destroys all existing sub-resources of the resource to be destroyed!


If false is passed to this command, the caller wants to delete the resource "logically". This means that the resource is restored or physically destroyed later. A soft-deleted content needs to support the command "undelete". This command brings it back to life. The implementation of the delete command can ignore the parameter and may opt to always destroy the resource physically.

Note pin.svg

Note:
Currently we do not have a trash service that could be used by UCB clients to manage soft-deleted contents.

Deleting a resource:

import com.sun.star.ucb.*;

{
    XContent xContent = ...

    /////////////////////////////////////////////////////////////////////
    // Destroy a resource physically...
    /////////////////////////////////////////////////////////////////////

    try {
        Boolean bDeletePhysically = new Boolean(true);

        // Execute command "delete".
        // using helper method executeCommand (see [CHAPTER:UCB.Using.Commands])
        executeCommand(xContent, "delete", bDeletePhysically);
    }
    catch (com.sun.star.ucb.CommandAbortedException e) {
        ... error ...
    }
    catch (com.sun.star.uno.Exception e) {
        ... error ...
    }
}

Copying, Moving and Linking

Copying, moving and creating links to a resource works differently from the other operations available for UCB Contents. There are three UCB Contents involved in these operations, the source object, target folder, and target object. There may even be two content Providers, for example, when moving a file located on an FTP server to the local file system of a workstation. Each implementation of the <idl>com.sun.star.ucb.UniversalContentBroker</idl> service supports the <idl>com.sun.star.ucb.XCommandProcessor</idl> interface. This command processor implements the command "globalTransfer" that can be used to copy and move UCB Contents, and create links to UCB Contents. The command takes an argument of type <idl>com.sun.star.ucb.GlobalTransferCommandArgument</idl>. To copy, move or create a link to a resource, execute the "globalTransfer" command at the UCB.

Note pin.svg

Note:
The reasons for the different handling are mainly technical. We did not want to force every single implementation of the transfer command of a UCB content to accept nearly all types of contents. Instead, we wanted to have one single implementation that would be able to handle all types of contents.

Copying, moving and creating links to a resource are shown in the following example:

import com.sun.star.ucb.*;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XInterface;

{
    String aSourceURL = ... // URL of the source object
    String aTargetFolderURL = ... // URL of the target folder

    /////////////////////////////////////////////////////////////////////
    // Obtain access to UCB...
    /////////////////////////////////////////////////////////////////////
    XInterface xUCB = ...

    // Obtain XCommandProcessor interface from UCB...
    XCommandProcessor xProcessor = (XCommandProcessor)UnoRuntime.queryInterface(
        XCommandProcessor.class, xUCB);

    if (xProcessor == null)
        ... error ...
    /////////////////////////////////////////////////////////////////////
    // Copy a resource to another location...
    /////////////////////////////////////////////////////////////////////
    try {
        GlobalTransferCommandArgument aArg = new GlobalTransferCommandArgument();
        aArg.TransferCommandOperation = TransferCommandOperation_COPY;
        aArg.SourceURL = aSourceURL;
        aArg.TargetURL = aTargetFolderURL;

        // object keeps it current name
        aArg.NewTitle = "";

        // fail,if object with same name exists in target folder
        aArg.NameClash = NameClash.ERROR;

        // Let UCB execute the command "globalTransfer",
        // using helper method executeCommand (see [CHAPTER:UCB.Using.Commands])
        executeCommand(xProcessor, "globalTransfer", aArg);
    }
    catch (com.sun.star.ucb.CommandAbortedException e) {
        ... error ...
    }
    catch (com.sun.star.uno.Exception e) {
        ... error ...
    }
}

UCB Configuration

This section describes how to configure the Universal Content Broker (UCB). Before a process uses the UCB, it needs to configure the UCB. Configuring the UCB means registering a set of Universal Content Providers (UCPs) at a content broker instance. Only UCPs known to the UCB are used to provide content. Generally we provide two ways to configure a UCB:

  • Create a default UCB with no UCPs registered and register all required UCPs manually.
  • Define a UCB configuration and create a UCB that is automatically configured with the UCPs given in that configuration.

UCP Registration Information

Before registering a content provider, the following information that is provided by the implementer of the UCP is required. The Appendix Universal Content Providers provides these for the currently available UCPs.

  • The UNO service name to instantiate the UCP, for example, "com.sun.star.ucb.FileContentProvider". Each UCP must be implemented and registered as a UNO component. Refer to chapter Writing UNO Components for more information on writing and registering UNO components.
  • An URL template used by the UCB to select the registered UCPs that best fit an incoming URL. See <idl>com.sun.star.ucb.XContentIdentifier</idl>. This can be the name of an URL scheme, for example, the file that selects the given UCP for all file URLs, or a limited regular expression, such as "http://"[^/?#]*".com"([/?#].*)? That will select the given UCP for all http URLs in the com domain. See the documentation of <idlm>com.sun.star.ucb.XContentProviderManager:registerContentProvider</idlm>() for details about these regular expressions.
  • Additional arguments that may be needed by the UCP.

Unconfigured UCBs

A UCB is called unconfigured if it has no content providers, thus it is not able to provide any contents. Each UCB implements the interface <idl>com.sun.star.ucb.XContentProviderManager</idl>. This interface offers the functionality to register UCPs at runtime.

To create an unconfigured UCB and configure it manually:

  1. Create an instance of the UNO service <idl>com.sun.star.ucb.UniversalContentBroker</idl>.
  2. Register the appropriate UCPs using the <idl>com.sun.star.ucb.XContentProviderManager</idl> interface of the UCB.

XContentProviderManager contains the following methods:

com::sun::star::ucb::XContentProvider registerContentProvider(
                [in] com::sun::star::ucb::XContentProvider Provider,
                [in] string Scheme,
                [in] boolean ReplaceExisting)
oneway void deregisterContentProvider(
                [in] com::sun::star::ucb::XContentProvider Provider,
                [in] string Scheme)
sequence< com::sun::star::ucb::ContentProviderInfo > queryContentProviders()
com::sun::star::ucb::XContentProvider queryContentProvider([in] string URL)

The XContentProvider configures a UCB for content providers, obtains <idl>com.sun.star.ucb.ContentProviderInfo</idl> structs describing the available providers, and the provider that is currently registered for a specific URL schema. The following example uses registerContentProvider() to configure an unconfigured UCB for a file content provider.

Unconfigured UCB:

import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.ucb.DuplicateProviderException;
import com.sun.star.ucb.XContentProvider;
import com.sun.star.ucb.XContentProviderManager;
import com.sun.star.uno.Exception;
import com.sun.star.uno.UnoRuntime;

boolean initUCB() {

    /////////////////////////////////////////////////////////////////////
    // Obtain Process Service Manager.
    /////////////////////////////////////////////////////////////////////

    XMultiServiceFactory xServiceFactory = ...

    /////////////////////////////////////////////////////////////////////
    // Create UCB. This needs to be done only once per process.
    /////////////////////////////////////////////////////////////////////

    XContentProviderManager xUCB;
    try {
        xUCB = (XContentProviderManager)UnoRuntime.queryInterface(
            XContentProviderManager.class, xServiceFactory.createInstance(
                "com.sun.star.ucb.UniversalContentBroker"));
    }
    catch (com.sun.star.uno.Exception e) {
    }

    if (xUCB == null)
        return false;

    /////////////////////////////////////////////////////////////////////
    // Instanciate UCPs and register at UCB.
    /////////////////////////////////////////////////////////////////////

    XContentProvider xFileProvider;
    try {
        xFileProvider = (XContentProvider)UnoRuntime.queryInterface(
            XContentProvider.class, xServiceFactory.createInstance(
                "com.sun.star.ucb.FileContentProvider"));
    }
    catch (com.sun.star.uno.Exception e) {
    }

    if (xFileProvider == null)
        return false;

    try {
        // Parameters: provider, URL scheme, boolean flag replaceExisting
        xUCB.registerContentProvider(xFileProvider, "file", new Boolean(false));
    }
    catch (DuplicateProviderException ex) {
    }

    // Create/register other UCPs...

    return true;
}

Preconfigured UCBs

A UCB is called preconfigured if it was given a UCB configuration at the time it was instantiated. A UCB configuration contains a set of UCP registration information.

To create a preconfigured UCB:

  1. Create an instance of the UNO service <idl>com.sun.star.ucb.UniversalContentBroker</idl>.
  2. Pass the configuration as a parameters to the creation function. The UCB instance returned offers all UCPs defined in the given configuration.

Preconfigured UCB:

import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.uno.Exception;
import com.sun.star.uno.XInterface;

boolean initUCB() {
    /////////////////////////////////////////////////////////////////////
    // Obtain Process Service Manager.
    /////////////////////////////////////////////////////////////////////

    XMultiServiceFactory xServiceFactory = ...

    /////////////////////////////////////////////////////////////////////
    // Create UCB. This needs to be done only once per process.
    /////////////////////////////////////////////////////////////////////

    XInterface xUCB;
    try {
        // Supply configuration to use for this UCB instance...
        String[] keys = new String[2];
        keys[0] = "Local";
        keys[1] = "Office";

        xUCB = xServiceFactory.createInstanceWithArguments(
            "com.sun.star.ucb.UniversalContentBroker", keys);
    }
    catch (com.sun.star.uno.Exception e) {
    }

    if (xUCB == null)
        return false;

    return true;
}

A UCB configuration used by a preconfigured UCB describes a set of UCPs available in a configuration. All UCPs contained in a configuration are registered at the UCB that is created using this configuration. A UCB configuration is identified by two keys that are strings. The keys allow some structuring in the configuration files, but they do not have a purpose. See the example file below. The standard configuration is "Local" and "Office", that allows access to all UCPs. The XML sample below shows how these keys are used to organize UCB configurations.

The predefined configurations for LibreOffice are defined in the file <OfficePath>/share/config/data/org/openoffice/ucb/Configuration.xcd. This file must be adapted to add configurations or edit existing configurations. The XCD file is used during the LibreOffice build process to generate the appropriate XML file. This XML file is part of a LibreOffice installation and is located in <OfficePath>share/config/registry/instance/org/openoffice/ucb/Configuration.xml. The UCB tries to get configuration data from this XML file.

UCB Configuration (org/openoffice/ucb/Configuration.xcd):

<!DOCTYPE schema:package SYSTEM "../schema/schema.description.dtd">
<schema:package package-id="org.openoffice.ucb.Configuration" xml:lang="en-US"
xmlns:schema="http://openoffice.org/2000/registry/schema/description"
xmlns:default="http://openoffice.org/2000/registry/schema/default"
xmlns:cfg="http://openoffice.org/2000/registry/instance">

<schema:templates template-id="org.openoffice.ucb.Configuration">

<!-- ContentProvider -->
<schema:group cfg:name="ContentProviderData">
<schema:value cfg:name="ServiceName" cfg:type="string">
</schema:value>
<schema:value cfg:name="URLTemplate" cfg:type="string">
</schema:value>
<schema:value cfg:name="Arguments" cfg:type="string">
</schema:value>
</schema:group>

<!-- ContentProvidersDataSecondaryKeys -->
<schema:group cfg:name="ContentProvidersDataSecondaryKeys">
<schema:set cfg:name="ProviderData"
    cfg:element-type="ContentProviderData"/>
</schema:group>

<!-- ContentProvidersDataPrimaryKeys -->
<schema:group cfg:name="ContentProvidersDataPrimaryKeys">
<schema:set cfg:name="SecondaryKeys"
    cfg:element-type="ContentProvidersDataSecondaryKeys"/>
</schema:group>
</schema:templates>

<schema:component cfg:writable="true"
component-id="org.openoffice.ucb.Configuration"
cfg:notified="true" cfg:localized="false">
<schema:set cfg:name="ContentProviders"
    cfg:element-type="ContentProvidersDataPrimaryKeys">
<default:group cfg:name="Local">
    <default:set cfg:name="SecondaryKeys"
    cfg:element-type="ContentProvidersDataSecondaryKeys">
    <default:group cfg:name="Office">
    <default:set cfg:name="ProviderData"
        cfg:element-type="ContentProviderData">

    <!-- Hierarchy UCP -->
    <default:group cfg:name="Provider1">
        <default:value cfg:name="ServiceName" cfg:type="string">
        <default:data>com.sun.star.ucb.HierarchyContentProvider</default:data>
        </default:value>
        <default:value cfg:name="URLTemplate" cfg:type="string">
        <default:data>vnd.sun.star.hier</default:data>
        </default:value>
        <default:value cfg:name="Arguments" cfg:type="string">
        <default:data/>
        </default:value>
    </default:group>

    <!-- File UCP -->
    <default:group cfg:name="Provider2">
        <default:value cfg:name="ServiceName" cfg:type="string">
        <default:data>com.sun.star.ucb.FileContentProvider</default:data>
        </default:value>
        <default:value cfg:name="URLTemplate" cfg:type="string">
        <default:data>file</default:data>
        </default:value>
        <default:value cfg:name="Arguments" cfg:type="string">
        <default:data/>
        </default:value>
    </default:group>

    <!-- Other UCPs go here -->

    </default:set>
    </default:group>
    </default:set>
</default:group>
</schema:set>
</schema:component>
</schema:package>

Content Provider Proxies

The UNO service implementing a UCP must be instantiated at the time the content provider is registered at the UCB. This is done using <idl>com.sun.star.ucb.XContentProviderManager</idl>'s registerContentProvider() method. In some cases, this can consume resources, because instantiating a UNO service means loading the libraries containing its code. As a convention, each UNO component should reside in its own library.

Therefore, a special UNO service is offered that provides a generic proxy for a UCP. Its purpose is to delay the loading of the real UCP code until it is needed. Generally, this does not happen before the first createContentIdentifier()/queryContent() calls are done at the proxy.

Instead of registering the real instantiated UCP at the UCB, a proxy is created for the UCP. The UCP registration information is passed to the proxy. The proxy only uses this information to instantiate the real UCP on demand. There is almost no performance overhead with this mechanism.

Note pin.svg

Note:
When using preconfigured UCBs, the UCB implementation uses proxies instead of the real UCPs to avoid wasting resources.

Heckert GNU white.svg

Content on this page is licensed under the Public Documentation License (PDL).