LibreOffice Developer's Guide: Chapter 14 - Universal Content Broker
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 com.sun.star.frame.Desktop 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
".
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.
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 com.sun.star.ucb.Content. The UCB content service interfaces include:
- com.sun.star.ucb.XContent
- com.sun.star.beans.XPropertyContainer
- com.sun.star.container.XChild (optional)
- com.sun.star.ucb.XCommandProcessor
- com.sun.star.ucb.XCommandProcessor2 (optional)
- com.sun.star.ucb.XContentCreator (optional)
The interface com.sun.star.ucb.XContent provides access to a content's type and identifier. The com.sun.star.ucb.XCommandProcessor 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 com.sun.star.beans.XPropertyContainer adds new properties to a content or removes properties that were previously added using this interface. The properties added are always made persistent.
The com.sun.star.ucb.XContentCreator 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 com.sun.star.container.XChild 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.
Some content commands defined by the UCB API are listed in the following table:
Selected Command Names for com.sun.star.ucb.XCommandProcessor | |
---|---|
"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 com.sun.star.ucb.ContentProvider. The UCP core interface is com.sun.star.ucb.XContentProvider. This interface facilitates the creation of content objects based on a given content identifier.
A UCB implements the service com.sun.star.ucb.UniversalContentBroker. The UCB core interfaces are com.sun.star.ucb.XContentProvider and com.sun.star.ucb.XContentProviderManager. The com.sun.star.ucb.XContentProvider interface implementation delegates requests to create content objects to the content provider registered for the supplied content identifier. The com.sun.star.ucb.XContentProviderManager interface is used to query the UCPs registered with a given UCB, and to register and remove UCPs.
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 com.sun.star.ucb.UniversalContentBroker, 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:
- Obtain access to the UCB.
- Let the UCB create a content identifier object for the requested URL using
createContentIdentifier()
at the com.sun.star.ucb.XContentIdentifierFactory of the UCB. - Let the UCB create a content object for the content identifier using
queryContent()
at the com.sun.star.ucb.XContentProvider 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 = UnoRuntime.queryInterface(
XContentIdentifierFactory.class, xUCB);
XContentProvider xProvider = 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 com.sun.star.ucb.XCommandProcessor interface. The execute() method at this interface expects a com.sun.star.ucb.Command, which is a struct containing the command name, command arguments and a handle:
Members of struct com.sun.star.ucb.Command | |
---|---|
Name | string, contains the name of the command |
Handle | long, contains an implementation-specific handle for the command |
Argument | 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 com.sun.star.ucb.Content and the UCP reference that comes with the SDK. For the latest information, visit ucb.openoffice.org.
If executing a command cannot proceed because of an error condition, the following occurs. If the execute call was supplied with a com.sun.star.ucb.XCommandEnvironment that contains a com.sun.star.task.XInteractionHandler, 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 = 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);
}
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 com.sun.star.beans.Property and returns an implementation of the interface com.sun.star.sdbc.XRow that is similar to a row of a JDBC resultset. To obtain property values from a UCB content:
- Define a sequence of properties you want to obtain the values for.
- Let the UCB content execute the command "
getPropertyValues
". - 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 com.sun.star.ucb.Command 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 com.sun.star.sdbc.XRow 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 com.sun.star.beans.PropertyValue 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 com.sun.star.ucb.OpenCommandArgument2. The value returned is an implementation of the service com.sun.star.ucb.DynamicResultSet. 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 com.sun.star.ucb.XDynamicResultSet interface. The static result set is a com.sun.star.sdbc.XResultSet that can be seen as a table, where each row contains a child content of the folder. Use the appropriate methods of com.sun.star.sdbc.XResultSet 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 com.sun.star.ucb.XContentAccess, 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 com.sun.star.beans.Property 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 com.sun.star.sdbc.XRow. Refer to the documentation of com.sun.star.ucb.OpenCommandArgument2 for more information about other parameters that can be passed to the "open
" command.
To access the children of a UCB content:
- Fill the com.sun.star.ucb.OpenCommandArgument2 structure according to your requirements.
- Let the UCB content execute the "
open
" command. - 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 = UnoRuntime.queryInterface(
XContentAccess.class, xResultSet);
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 com.sun.star.ucb.OpenCommandArgument2. 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, com.sun.star.io.XActiveDataSink and com.sun.star.io.XOutputStream.
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 com.sun.star.io.XInputStream 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 ofXOutputStream
:
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 com.sun.star.io.XOutputStream 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 com.sun.star.io.XActiveDataSink, then any necessary buffering takes place in the implementation of the open command.
The following example shows a possible implementation of an com.sun.star.io.XActiveDataSink 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 com.sun.star.ucb.InsertCommandArgument and returns void. The caller supplies the implementation of an com.sun.star.io.XInputStream 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 com.sun.star.ucb.XContentCreator 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 com.sun.star.ucb.XContentCreator 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 com.sun.star.ucb.XContentCreator 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 com.sun.star.ucb.InsertCommandArgument 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:
- Obtain the interface com.sun.star.ucb.XContentCreator from a suitable UCB content.
- Call
createNewContent()
at the content creator. Supply information on the type of content to create with the arguments. The argument expected is a com.sun.star.ucb.ContentInfo struct. - Obtain and set the property values that are mandatory for the content just created.
- 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 = 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 com.sun.star.ucb.ContentInfo types for creatable contents. Additionally, you can ask the content creator for its creatable contents using queryCreatableContentsInfo(). 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.
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.
Deleting a resource:
import com.sun.star.ucb.*;
{
XContent xContent = ...
/////////////////////////////////////////////////////////////////////
// Destroy a resource physically...
/////////////////////////////////////////////////////////////////////
try {
Boolean bDeletePhysically = 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 com.sun.star.ucb.UniversalContentBroker service supports the com.sun.star.ucb.XCommandProcessor 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 com.sun.star.ucb.GlobalTransferCommandArgument. To copy, move or create a link to a resource, execute the "globalTransfer
" command at the UCB.
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 = 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 com.sun.star.ucb.XContentIdentifier. 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 registerContentProvider() 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 com.sun.star.ucb.XContentProviderManager. This interface offers the functionality to register UCPs at runtime.
To create an unconfigured UCB and configure it manually:
- Create an instance of the UNO service com.sun.star.ucb.UniversalContentBroker.
- Register the appropriate UCPs using the com.sun.star.ucb.XContentProviderManager 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 com.sun.star.ucb.ContentProviderInfo 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 = 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 = 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", 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:
- Create an instance of the UNO service com.sun.star.ucb.UniversalContentBroker.
- 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 com.sun.star.ucb.XContentProviderManager'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.