Using Services

From Apache OpenOffice Wiki
< Documentation‎ | DevGuide
Revision as of 09:49, 28 September 2007 by Cc208500 (Talk)

Jump to: navigation, search

The concepts of interfaces and services were introduced for the following reasons:

Interfaces and services separate specification from implementation

The specification of an interface or service is abstract, that is, it does not define how objects supporting a certain functionality do this internally. Through the abstract specification of the API, it is possible to pull the implementation out from under the API and install a different implementation if required.

Service names allow to create instances by specification name, not by class names

In Java or C++ you use the new operator to create a class instance. This approach is restricted: the class you get is hard-coded. You cannot later on exchange it by another class without editing the code. The concept of services solves this. The central object factory in the global service manager, is asked to create an object that can be used for a certain purpose without defining its internal implementation. This is possible, because a service can be ordered from the factory by its service name and the factory decides which service implementation it returns. Which implementation you get makes no difference, you only use the well-defined interface of the service.

Multiple-inheritance interfaces make fine-grained interfaces manageable

Abstract interfaces are more reusable if they are fine-grained, i.e., if they are small and describe only one aspect of an object, not several aspects. But then you need many of them to describe a useful object. Multiple-inheritance interfaces allow to have fine-grained interfaces on the one hand and to manage them easily by forging them into a collection. Since it is quite probable that objects in an office environment will share many aspects, this fine granularity allows the interfaces to be reused and thus to get objects that behave consistently. For instance, it was possible to realize a unified way to handle text, no matter if you are dealing with body text, text frames, header or footer text, footnotes, table cells or text in drawing shapes. It was not necessary to define separate interfaces for all of these purposes.

Let us consider the old-style service in UML notation. The UML chart shown below depicts the mandatory interfaces of a TextDocument service. These interfaces express the basic aspects of a text document in It contains text, it is searchable and refreshable. It is a model with URL and controller, and it is modifiable, printable and storable. The UML chart shows how this is specified in the API.

Text Document

On the left side of the illustration, the services and are shown. Every TextDocument must include these services by definition.

On the right side of the illustration, you find the interfaces, that the services must export. Their method compartments list the methods contained in the various interfaces. In the API, all interface names have to start with an X to be distinguishable from the names of other entities.

Every TextDocument object must support three interfaces: XTextDocument, XSearchable, and XRefreshable. In addition, because a TextDocument is always an OfficeDocument, it must also support the interfaces XPrintable, XStorable, XModifiable and XModel. The methods contained in these interfaces cover these aspects: printing, storing, modification and model handling.

Note that the interfaces shown in the illustration are only the mandatory interfaces of a TextDocument. A TextDocument has optional properties and interfaces, among them the properties CharacterCount, ParagraphCount and WordCount and the XPropertySet interface which must be supported if properties are present at all. The current implementation of the TextDocument service in does not only support these interfaces, but all optional interfaces as well. The usage of a TextDocument is described thoroughly in Text Documents.

Using Interfaces

The fact that every UNO object must be accessed through its interfaces has an effect in languages like Java and C++, where the compiler needs the correct type of an object reference before you can call a method from it. In Java or C++, you normally just cast an object before you access an interface it implements. When working with UNO objects this is different: You must ask the UNO environment to get the appropriate reference for you whenever you want to access methods of an interface which your object supports, but your compiler does not yet know about. Only then you can cast it safely.

The Java UNO environment has a method queryInterface() for this purpose. It looks complicated at first sight, but once you understand that queryInterface() is about safe casting of UNO types across process boundaries, you will soon get used to it. Take a look to the second example FirstLoadComponent where a new Desktop object is created and afterwards the queryInterface() method is used to get the XComponentLoader interface.

  Object desktop = xRemoteServiceManager.createInstanceWithContext(
                "", xRemoteContext);
  XComponentLoader xComponentLoader = (XComponentLoader)
                UnoRuntime.queryInterface(XComponentLoader.class, desktop);

We asked the service manager to create a using its factory method createInstanceWithContext(). This method is defined to return a Java Object type, which should not surprise you—after all the factory must be able to return any type:

  java.lang.Object createInstanceWithContext(String serviceName, XComponentContext context)

The object we receive is a service.

The following figure is a simplified specification in UML notation showing the relation to the service and the supported interfaces.The point is, while we know that the object we ordered at the factory is a DesktopUnoUrlResolver and exports among other interfaces the interface XComponentLoader, the compiler does not. Therefore, we have to use the UNO runtime environment to ask or query for the interface XComponentLoader, since we want to use the loadComponentFromURL() method on this interface. The method queryInterface() makes sure we get a reference that can be cast to the needed interface type, no matter if the target object is a local or a remote object. There are two queryInterface definitions in the Java UNO language binding:

  java.lang.Object UnoRuntime.queryInterface(java.lang.Class targetInterface, Object sourceObject)
  java.lang.Object UnoRuntime.queryInterface( targetInterface, Object sourceObject)

Since UnoRuntime.queryInterface() is specified to return a java.lang.Object just like the factory method createInstanceWithContext(), we still must explicitly cast our interface reference to the needed type. The difference is that after queryInterface() we can safely cast the object to our interface type and, most important, that the reference will now work even with an object in another process. Here is the queryInterface() call, explained step by step:

  XComponentLoader xComponentLoader = (XComponentLoader)
                  UnoRuntime.queryInterface(XComponentLoader.class, desktop);

XComponentLoader is the interface we want to use, so we define a XComponentLoader variable named xComponentLoader (lower x) to store the interface we expect from queryInterface. Then we query our desktop object for the XComponentLoader interface, passing in XComponentLoader.class as target interface and desktop as source object. Finally we cast the outcome to XComponentLoader and assign the resulting reference to our variable xComponentLoader. If the source object does not support the interface we are querying for, queryInterface() will return null.

In Java, this call to queryInterface() is necessary whenever you have a reference to an object which is known to support an interface that you need, but you do not have the proper reference type yet. Fortunately, you are not only allowed to queryInterface() from java.lang.Object source types, but you may also query an interface from another interface reference, like this:

  // loading a blank spreadsheet document gives us its XComponent interface:
  XComponent xComponent = xComponentLoader.loadComponentFromURL(
  "private:factory/scalc", "_blank", 0, loadProps);
  // now we query the interface XSpreadsheetDocument from xComponent
  XSpreadsheetDocument xSpreadsheetDocument = (XSpreadsheetDocument)UnoRuntime.queryInterface(
                  XSpreadsheetDocument.class, xComponent);

Furthermore, if a method is defined in such a way that it already returns an interface type, you do not need to query the interface, but you can use its methods right away. In the snippet above, the method loadComponentFromURL is specified to return an interface, so you may call the XComponent methods addEventListener() and removeEventListener() directly at the xComponent variable, if you want to be notified that the document is being closed. The corresponding step in C++ is done by a Reference<> template that takes the source instance as parameter:

  // instantiate a sample service with the servicemanager.
  Reference< XInterface > rInstance =
  OUString::createFromAscii("" ),
  rComponentContext );
  // Query for the XComponentLoader interface
  Reference< XComponentLoader > rComponentLoader( rInstance, UNO_QUERY );

In Basic, querying for interfaces is not necessary, the Basic runtime engine takes care about that internally. With the proliferation of multiple-inheritance interfaces in the API, there will be less of a demand to explicitly query for specific interfaces in Java or C++. For example, with the hypothetical interfaces

  interface XBase1 {   void fun1();
  interface XBase2 {
      void fun2();
  interface XBoth { // inherits from both XBase1 and XBase2
      interface XBase1;
      interface XBase2;
  interface XFactory {
      XBoth getBoth();};

you can directly call both fun1() and fun2() on a reference obtained through XFactory.getBoth(), without querying for either XBase1 or XBase2.

Using Properties

An object must offer its properties through interfaces that allow you to work with properties. The most basic form of these interfaces is the interface There are other interfaces for properties, such as, that gets and sets a multitude of properties with a single method call. The XPropertySet is always supported when properties are present in a service.

In XPropertySet, two methods carry out the property access, which are defined in Java as follows:

  void setPropertyValue(String propertyName, Object propertyValue)
  Object getPropertyValue(String propertyName)

In the FirstLoadComponent example, the XPropertySet interface was used to set the CellStyle property at a cell object. The cell object was a and therefore supports also the service which had a property CellStyle. The following code explains how this property was set:

  // query the XPropertySet interface from cell object
  XPropertySet xCellProps = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xCell);
  // set the CellStyle property
  xCellProps.setPropertyValue("CellStyle", "Result");

You are now ready to start working with a document.

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