[ Previous document | Content Table | Next document ]

16    Office Bean

16.1    Introduction

This chapter describes the OfficeBean Java Bean component. It is assumed that the reader is familiar with the Java Beans technology. Additional information about Java Beans can be found at http://java.sun.com/beans.

With the OfficeBean, a developer can easily write Java applications, harnessing the power of OpenOffice.org. It encapsulates a connection to a locally running OpenOffice.org process, and hides the complexity of establishing and maintaining that connection from the developer.

It also allows embedding of OpenOffice.org documents within the Java environment. It provides an interface the developer can use to obtain Java AWT windows into which the backend OpenOffice.org process draws its visual representation. These windows are then plugged into the UI hierarchy of the hosting Java application. The embedded document is controlled from the Java environment, since the OfficeBean allows developers to access the complete OpenOffice.org API from their Java environment giving them full control over the embedded document, its appearance and behavior.

The OfficeBean consists of two parts. The officebean.jar implements a fundamental framework that is able to connect to the office and display the application window of a local OpenOffice.org installation in a Swing frame. The other part has three example beans that take advantage of the officebean.jar to implement Java beans to display the OpenOffice.org documents. The example beans are the BasicOfficeBean, SimpleBean and OfficeWriter.

The BasicOfficeBean is a java.awt.Container that encapsulates office documents. It connects to the office, loads documents, tracks their modified status and saves them.

The SimpleBean and OfficeWriter are derived from BasicOfficeBean. The SimpleBean is used to toggle the menu bar. The OfficeWriter wraps a writer document, for example, by providing its content as a string and as XText interface, and is an extension of Office that is based on the BasicOfficeBean. The Office enhances BasicOfficeBean by handling streams, controlling toolbars and menu bar, publishing the global service manager, and returning the current selection.

The code snippet below shows how to use the OfficeBean API to display a OpenOffice.org document in a Java environment using the example class SimpleBean. It creates a SimpleBean, applies an OfficeConnection to it and adds the connected office bean to a frame. When it is added to a frame, SimpleBean loads a OpenOffice.org document from a URL, such as private:factory/swriter:

public loadDocument(String url) {

    Frame f = new Frame();

    OfficeConnection officeConnection = new LocalOfficeConnection();

    SimpleBean simpleBean = new SimpleBean();

    try {

        simpleBean.setOfficeConnection(officeConnection);

        f.add(simpleBean, BorderLayout.CENTER);

        simpleBean.load(url);  

    } catch (Exception e) {

        e.printStackTrace();

    }

}

shows the resulting OpenOffice.org document window embedded within a java.awt.Frame.


Illustration 1.1

16.2    Overview of the OfficeBean API

The OfficeBean API is exported in two Java interfaces, com.sun.star.beans.OfficeConnection and com.sun.star.beans.OfficeWindow.


Note that these interfaces are Java interfaces in the com.sun.star.beans package, they are not UNO interfaces.

An implementation of com.sun.star.beans.OfficeConnection is provided in the class com.sun.star.beans.LocalOfficeConnection. The class com.sun.star.beans.LocalOfficeWindow implements com.sun.star.beans.OfficeWindow. The relationship between the OfficeBean interfaces and their implementation classes is shown in the illustration below.


Illustration 1.2

The following sections describe the OfficeBean interfaces OfficeConnection and OfficeWindow.Refer to the section "Using the OfficeBean" for an explanation of how the implementation classes are used.

16.2.1    OfficeConnection Interface

The com.sun.star.beans.OfficeConnection interface contains the methods used to configure, initiate,and manage the connection to OpenOffice.org. These methods are:

public void setUnoUrl(String URL) throws java.net.MalformedURLException

public com.sun.star.uno.XComponentContext getComponentContext()

public OfficeWindow createOfficeWindow(Container container)

public void setContainerFactory(ContainerFactory containerFactory)

The client uses setUnoUrl() to specify to the OfficeBean how it connects to the OpenOffice.org process. See the section “Configuring the OfficeBean” for a description of the syntax of the URL. A java.net.MalformedURLException is thrown by the concrete implementation if the client passes a badly formed URL as an argument.

The method getComponentContext() gets an object that implements the com.sun.star.uno.XComponentContext interface from the OfficeBean. This object is then used to obtain objects implementing the full OpenOffice.org API from the backend OpenOffice.org process.

A call to createOfficeWindow() requests a new OfficeWindow from the OfficeConnection. The client obtains the java.awt.Component from the OfficeWindow to plug into its UI. See the getAWTComponent() method below on how to obtain the Component from the OfficeWindow. The client provides java.awt.Container that indicates to the implementation what kind of OfficeWindow it is to create.

The method setContainerFactory() specifies to the OfficeBean the factory object it uses to create Java AWT windows to display popup windows in the Java environment. This factory object implements the com.sun.star.beans.ContainerFactory interface. See below for a definition of the ContainerFactory interface.

If the client does not implement its own ContainerFactory interface, the OfficeBean uses its own default ContainerFactory creating instances of java.awt.Canvas.

16.2.2    OfficeWindow Interface

The com.sun.star.beans.OfficeWindow interface encapsulates the relationship between the AWT window that the client plugs into its UI, and the com.sun.star.awt.XWindowPeer object which the OpenOffice.org process uses to draw into the window. It provides two public methods:

public java.awt.Component getAWTComponent()

public com.sun.star.awt.XWindowPeer getUNOWindowPeer()

The client uses getAWTComponent() to obtain the Component window associated with an OfficeWindow. This Component is then added to the clients UI hierarchy.

The method getUNOWindowPeer() obtains the UNO com.sun.star.awt.XWindowPeer object associated with an OfficeWindow.

16.2.3    ContainerFactory Interface

The interface com.sun.star.beans.ContainerFactory defines a factory class the client implements if it needs to control how popup windows generated by the backend OpenOffice.org process are presented within the Java environment. The factory has only one method:

public java.awt.Container createContainer()

It returns a java.awt.Container.


For more background on handling popup windows generated by OpenOffice.org, and possible threading issues to consider, see 6.1.8 Office Development - OpenOffice.org Application Environment - Java Window Integration.

16.3    LocalOfficeConnection and LocalOfficeWindow

The class LocalOfficeConnection implements a connection to a locally running OpenOffice.org process that is an implementation of the interface OfficeConnection. Its method createOfficeWindow() creates an instance of the class LocalOfficeWindow, that is an implementation of the interface OfficeWindow.

Where LocalOfficeConnection keeps a single connection to the OpenOffice.org process, there are multiple, shared LocalOfficeWindow instances for multiple beans. The LocalOfficeWindow implements the embedding of the local OpenOffice.org document window into a java.awt.Container.

16.4    Configuring the OfficeBean


Illustration 1.3

The fundamental framework of the OfficeBean is contained in the officebean.jar archive file that depends on a local library officebean.dll or libofficebean.so, depending on the platform. The interaction between the backend OpenOffice.org process, officebean local library, OfficeBean and the Java environment is shown in the illustration below.

The OfficeBean allows the developer to connect to and communicate with the OpenOffice.org process through a named pipe. It also starts up a OpenOffice.org instance if it cannot connect to a running office. This is implemented in the OfficeBean local library. The OfficeBean depends on three configuration settings to make this work. It has to find the local library, needs the location of the OpenOffice.org executable, and the bean and office must know the pipe name to use.

16.4.1    Default Configuration

The OfficeBean uses default values for all the configuration settings, if none are provided:

    1. The library sal3 (Windows: sal3.dll, Unix: libsal3.so) is located in the <OfficePath>/program folder. It maybe necessary to add the <OfficePath>/program folder to the PATH environment variable if the bean cannot find sal3.

    2. The library jawt.dll is needed in Windows. If the bean cannot find it, check the Java Runtime Environment binaries (<JRE>/bin) in your PATH environment variable.

Based on these default values, the OfficeBean tries to start the office in listening mode with the -accept commandline option. The exact parameters used by the bean are:

# WINDOWS

soffice.exe -bean -accept=pipe,name=<user.name>_Office;urp;StarOffice.NamingService

# UNIX

soffice -bean "-accept=pipe,name=<user.name>_Office;urp;StarOffice.NamingService"

There is a limitation in the communication process with the current OfficeBean and OpenOffice.org. If a OpenOffice.org process is already runningthat was not started with the proper ‑accept=pipe option, the OfficeBean does not connect to it. It opens a Writer document outside of the Java frame. The OpenOffice.org process has to be started so that it opens a properly named pipe to enable OpenOffice.org to be displayed as an embedded OfficeBean and top-level OpenOffice.org window. This is achieved by editing the file <OfficePath>\user\config\registry\instance\org\openoffice\Setup.xml. Within the <Office/> element, the developer adds an <ooSetupConnectionURL/> element with settings for a named pipe. The following example shows a user-specific Setup.xml that configures a named pipe for a user named JohnDoe:

<?xml version="1.0" encoding="UTF-8"?>

<Setup state="modified" cfg:package="org.openoffice"

  xmlns="http://openoffice.org/2000/registry/components/Setup"

  xmlns:cfg="http://openoffice.org/2000/registry/instance"

  xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance">

 <Office>

  <ooSetupConnectionURL cfg:type="string">

    pipe,name=JohnDoe_Office;urp;StarOffice.NamingService

  </ooSetupConnectionURL>

  <Factories cfg:element-type="Factory">

   <Factory cfg:name="com.sun.star.text.TextDocument">

     <ooSetupFactoryWindowAttributes cfg:type="string">

       193,17,1231,1076;1;                         

     </ooSetupFactoryWindowAttributes>

   </Factory>

  </Factories>

 </Office>

</Setup>

With this user-specific Setup.xml file, the office opens a named pipe JohnDoe_Office whenever it starts up. It does not matter if the user double clicks a document, runs the Quickstarter, or starts a new, empty document from a OpenOffice.org template.


If you write Java clients, the named pipe can only be used with the OfficeBean. There is no pure Java implementation for named pipes in the Java UNO runtime. Furthermore, configure the office to use a named pipe or listen for connections on a socket. The office cannot use pipe and socket at the same time.

16.4.2    Customized Configuration

Besides these default values, the OfficeBean is configured to use other parameters. There are three possibilities,using a UNO URL with path and pipe name parameters, setting Java system properties at runtime, or creating a Java property file in the user.home directory.

The first method that a developer uses to configure the OfficeBean is through the UNO URL passed in the setUnoUrl() call. The syntax of the UNO URL is as follows:

    url    := 'uno:localoffice'[','<params>]';urp;StarOffice.NamingService'

    params := <path>[','<pipe>]

    path   := 'path='<pathv>

    pipe   := 'pipe='<pipev>

    pathv  := platform_specific_path_to_the_local_office_distribution

    pipev  := local_office_connection_pipe_name

Here is an example of how to use setUnoUrl() in code:

    OfficeConnection officeConnection = new LocalOfficeConnection();

    officeConnection.setUnoUrl(

        “uno:localoffice,path=/home/user/staroffice6.0/program;urp;StarOffice.NamingService”);

The second method that is used to configure the OfficeBean is using Java system properties. The properties supported by the OfficeBean are:

Properties supported by the OfficeBean

com.sun.star.beans.path

Specifies the path to the program directory of the OpenOffice.org installation.

com.sun.star.beans.libpath

Specifies the directory the OfficeBean local library (Windows: officebean.dll, Unix: libofficebean.so) is stored

The libpath property seems to be ignored, at least on Windows

These properties are set through a call to System.setProperty() before creating the OfficeConnection, for example:

    System.setProperty("com.sun.star.beans.path", "/home/user/staroffice6.0/program");

    System.setProperty("com.sun.star.beans.libpath", "/home/user/lib");

    OfficeConnection officeConnection = new LocalOfficeConnection();

The properties are also set by creating a file .officebean.properties on Unix or officebean.properties with no leading dot on Windows in the directory corresponding to the platform specific user.home Java property, that is, the directory returned by System.getProperty("user.home").


In NetBeans, you can look up the home directory in the Detail tab of the Help - About dialog. Look for the Home dir entry.

A possible officebean.properties file on a Windows machine may look like the following:

    com.sun.star.beans.path=D:\\StarOffice6.0\\program

    com.sun.star.beans.libpath=X:\\SDK\\windows\\bin

On Unix possible .officebean.properties could be:

    com.sun.star.beans.path=/home/user/staroffice6.0/program

    com.sun.star.beans.libpath=/home/user/lib

16.5    Using the OfficeBean

The officebean.jar Java archive file provided as part of the OpenOffice.org Software Development Kit provides an implementation of the OfficeBean API that developers can use to develop applications embedding OpenOffice.org functionality. The implementation is provided in the previously mentioned classes LocalOfficeConnection and LocalOfficeWindow.

As previously mentioned, the OfficeBean API and implementation is a low-level interface providing the building blocks necessary for developing OfficeBeans. The examples directory of the OpenOffice.org Software Development Kit contains examples of these beans that show how to use these building blocks to build and embed an OfficeBean in a Java applet or application. To use the low-level API to communicate with the backend OpenOffice.org process, use the LocalOfficeConnection and LocalOfficeWindow classes directly. Use or extend the beans in the examples to avoid the complexity of using the low-level API.

Basic bean functionality is provided in the abstract class BasicOfficeBean that is subclassed to create beans supporting OpenOffice.org document loading and storing from Java. The BasicOfficeBean is a java.awt.Container, therefore subclasses of BasicOfficeBean are plugged directly into a Java UI hierarchy.


Since the OfficeBean uses a native peer to render OpenOffice.org documents, Swing components, such as drop-down menus or list boxes appear behind it, or r they are not displayed at all! To avoid this, exclusively employ AWT components when using the OfficeBean.


Illustration 1.4


For readability, the try or catch blocks have been removed from the examples below. The full source code of the examples is found in the OfficeBean directory of the SDK Java examples in <SDK>/examples/DevelopersGuide/OfficeBean.

16.5.1    SimpleBean Example

The SimpleBean is a concrete subclass of BasicOfficeBean used on the component palette of Java IDEs, such as NetBeans (http://www.netbeans.org).

Using SimpleBean

To use SimpleBean, build the SimpleBean example from <SDK>/examples/java/OfficeBean/SimpleBean.

Installing SimpleBean

Before building and installing the SimpleBean, install and configure the following tools.

Java Development Kit (JDK)

A JDK from version 1.3.1 is required to build SimpleBean.

OpenOffice.org Software Development Kit (SDK)

An installation of the OpenOffice.org Software Development Kit (SDK) is required in your development environment. Refer to the SDK documentation for details on how to install the SDK.

GNU make

The SDK depends on a GNU make version 3.79 or later found at www.gnu.org, and the local windows executables are available from www.nextgeneration.dk or unxutils.sourceforge.net. Note that on Windows the current Cygwin make is known to cause problems with the OpenOffice.org SDK.

After setting up the SDK, start a commandline shell, change to the SDK folder and run the setsdkenv script created by configure. This script creates a number of environment variables that are required for the make process. Change to the SimpleBean directory and execute make. The make utility runs the makefile in the current folder that creates a simplebean.jar in the platform-specific .out folder of the SDK. The following steps are a possible method to install SimpleBean into the NetBeans IDE.


Illustration 1.5

Putting SimpleBean to Work

The following example applet SimpleViewer demonstrates the usage of SimpleBean. It employs an instance of SimpleBean to load a OpenOffice.org document. (OfficeBean/SimpleBean/SimpleViewer.java)

public class SimpleViewer extends java.applet.Applet {

    private OfficeConnection localOfficeConnection;

    private SimpleBean simpleBean;

    public void init() {

        setLayout(new BorderLayout());

   

        // initialize the rest of the Java GUI including a button to create a blank document

        // the method createNewDocument will be called when it is clicked

        // create a SimpleBean and add it to our applet

        simpleBean = new SimpleBean();

        add(simpleBean, BorderLayout.CENTER);

    }

    // load a document

    public void createNewDocument(String url) {

        // if there is no connection to an Office process we need to connect to one

        if (localOfficeConnection == null)

            localofficeConnection = new LocalOfficeConnection();

       

        // tell the bean to use localOfficeConnection

        simpleBean.setOfficeConnection(localofficeConnection);

        // ask the bean to load the file in url

        simpleBean.load(url);

    }

}

In Netbeans, create a new java.awt.Frame, drop SimpleBean in, and edit the Frame constructor to initialize SimpleBean.

public class SimpleBean1 extends java.awt.Frame {

   

    /** Creates new form SimpleBean1 */

    public SimpleBean1() {

        initComponents();

        // create Connection

        com.sun.star.beans.OfficeConnection connection1 =

            new com.sun.star.beans.LocalOfficeConnection();

        // set up simpleBean1 with connection1

        simpleBean1.setOfficeConnection(connection1);

        // load blank document

        try {

            simpleBean1.load("private:factory/swriter");       

        } catch (Exception ex) {

            ex.printStackTrace();

        }

    }

    ...

}

SimpleBean Internals

 Let us look at how BasicOfficeBean loads a document to understand how a bean uses the OfficeBean API. The BasicOfficeBean.load() method checks if an OfficeWindow has been created and calls the BasicOfficeBean.openConnection() method to create one, if required:

public synchronized void load(String url) throws java.io.IOException {

    try {

        if (mWindow == null)

                openConnection();

    // we consider the complete load() method later

    ...

The BasicOfficeBean.openConnection() uses the com.sun.star.beans.OfficeConnection passed in the setOfficeConnection() call, that is, an instance of com.sun.star.beans.LocalOfficeConnection, to create an OfficeWindow. An instance of the com.sun.star.lang.XMultiServiceFactory interface is also obtained through the OfficeConnection and stored in the BasicBean instance: (OfficeBean/BasicOfficeBean.java)

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

    if (mWindow != null)

        return;

    // Obtain the global MultiServiceFactory and store it in mServiceFactory

    XMultiComponentFactory  compfactory;

    compfactory  = mConnection.getComponentContext().getServiceManager();

    mServiceFactory  = (XMultiServiceFactory)UnoRuntime.queryInterface(

        XMultiServiceFactory.class, compfactory);

    // Create the OfficeWindow

    mWindow  = mConnection.createOfficeWindow(this);

    // Create the office document frame and initialize the bean

    initialize();

}

The BasicOfficeBean.initialize() is the last call in openConnection() that sets up the objects necessary for document loading: An instance of com.sun.star.frame.Frame is initialized with the UNO window peer, and the interfaces com.sun.star.lang.XMultiServiceFactory and com.sun.star.document.XTypeDetection of a com.sun.star.frame.FrameLoaderFactory. (OfficeBean/BasicOfficeBean.java)

private void initialize() {

    // Get XWindow interface of UNO window peer

    XWindow window = (XWindow)UnoRuntime.queryInterface(XWindow.class, mWindow.getUNOWindowPeer());

    // instantiate UNO frame, store its XFrame interface in mFrame

    object = mServiceFactory.createInstance(“com.sun.star.frame.Frame");

    mFrame = (Xframe)UnoRuntime.queryInterface(XFrame.class, object);

    // configure the frame to use the UNO window peer

    mFrame.initialize(window);

 

    // instantiate frame loader factory

    object = mServiceFactory.createInstance("com.sun.star.frame.FrameLoaderFactory");

    // store its XMultiServiceFactory interface in mFrameLoaderFactory

    mFrameLoaderFactory = (XMultiServiceFactory)UnoRuntime.queryInterface(

        XMultiServiceFactory.class, object);

    // store its XTypeDetection interface in mTypeDetector

    mTypeDetector = (XTypeDetection)UnoRuntime.queryInterface(XTypeDetection.class, object);

}

The BasicOfficeBean.load() method then completes the loading of the document. Instead of loadComponentFromURL(), a com.sun.star.frame.FrameLoader is employed that expects a frame to load the document into. The following snippet shows the complete load() method: (OfficeBean/BasicOfficeBean.java)

public synchronized void load(String url) throws java.io.IOException {

    try {

        if (mWindow == null)

            openConnection();

        // Make sure the URL contains something meaningful

        if (url.equals(""))

            url = getDefaultDocumentURL();

        // Find out the type of the document.

        String type = mTypeDetector.queryTypeByURL(url);

        // Get frame loader factory for document type

        Object object = mFrameLoaderFactory.createInstance(type);

        XSynchronousFrameLoader frameLoader;

        frameLoader = (XSynchronousFrameLoader)UnoRuntime.queryInterface(

            XSynchronousFrameLoader.class, object);

        // Create the document descriptor with two PropertyValue structs:

        // FileName = url and TypeName = type

        PropertyValue[] desc = new PropertyValue[2];

        desc[0] = new PropertyValue("FileName", 0, url, PropertyState.DIRECT_VALUE);

        desc[1] = new PropertyValue("TypeName", 0, type, PropertyState.DIRECT_VALUE);

        // Avoid Dialog 'Document changed' while reloading

        try {

            setModified(false);

        } catch (java.lang.IllegalStateException exp) {

        }

        // Load the document and store the URL

        if (frameLoader.load(desc, mFrame) == false) {

            throw new java.io.IOException("Can not load a document: \"" + url + "\"");

        }

        mDocumentURL = url;

        // Get document's XModifiable interface if any and store it

        if ((mFrame != null) && (mFrame.getController() != null)) {

            XModel model = mFrame.getController().getModel();

            mModifiable = (Xmodifiable)UnoRuntime.queryInterface(XModifiable.class, model);

        }

        else {

            mModifiable = null;

        }

        // Find top most parent and force it to validate.

        Container parent = this;

        while (parent.getParent() != null)

            parent = parent.getParent();

        ((Window)parent).validate();

    }

    catch (com.sun.star.uno.Exception exp) {

        throw new java.io.IOException(exp.getMessage());

    }

}

16.5.2    OfficeWriterBean Example

The DocViewer is a Java application that loads a new or opens an existing Writer document, and prints the selected text into a text field. It updates the text field as the selected text changes. The DocViewer depends on two bean classes to display and manipulate the document: Office and OfficeWriter.

The Office and OfficeWriter are concrete classes that show how the BasicOfficeBean is extended to allow the developer to manipulate an embedded document programmatically. The Office shows how to add the ability to execute commands on the backend OpenOffice.org process using the dispatch framework. Refer to chapter 6 Office Development). It uses instances of the OfficeCommand class, also provided with the examples, to represent OpenOffice.org command URLs that are applied to the document. The Office.setMenuBarVisible() is an example of how to do this: (OfficeBean/OfficeWriterBean/Office.java)

public void setMenuBarVisible(boolean visible) {

    if (mMenuBarVisible != visible) {

        if (isDocumentLoaded() == true) {

            OfficeCommand command = new OfficeCommand(SID_TOGGLEMENUBAR);

            command.appendParameter("MenuBar", new Boolean(visible));

            command.execute(this);

        }

        mMenuBarVisible  = visible;

        firePropertyChange("MenuBarVisible", new Boolean(mMenuBarVisible), new Boolean(visible));

    }

}

OfficeWriter extends Office further to allow the developer to get and set the contents of the document, obtain the com.sun.star.text.XTextDocument interface for the document, get the selected text in the document, and listen for changes to the document. The code below shows how OfficeWriter is added to the DocViewer UI, and how DocViewer listens for changes to the document loaded by OfficeWriter: (OfficeBean/OfficeWriterBean/DocViewer.java)

public class DocViewer {

    public void initUI() {

        Frame frame = new Frame();

        JTextField currentSelection = new JTextField();

   

        // set up the rest of the application UI

   

        mOfficeWriter = new OfficeWriter();

        mOfficeWriter.setOfficeConnection(new LocalOfficeConnection());

        mOfficeWriter.addSelectionChangeListener(new DocViewerChangeListener());

        frame.add(mOfficeWriter, BorderLayout.CENTER);

    }

The following shows the ChangeListener that DocViewer uses to listen for changes in the document selection:(OfficeBean/OfficeWriterBean/DocViewer.java)

    class DocViewerChangeListener implements ChangeListener {

        public void stateChanged(ChangeEvent event)

        {

            String s = mOfficeWriter.getSelection();

            currentSelection.setText(s);

        }

    }

The examples above provide an overview of how the OfficeBean API is used to create Java beans that can be used in Java applications and applets. BeanInfo classes are provided for the SimpleBean, Office and OfficeWriter for integrating within an IDE (Integrated Development Environment), such as the Bean Development Kit or Forte for Java. Developers can use the examples as a guideline when using the OfficeBean API to write new beans, or use or extend the example beans.

[ Previous document | Content Table | Next document ]