Using OpenOffice.org from Java Applications
A UNO Quickstart
OpenOffice.org is quite nice as a free and open source office suite that works on Linux and Windows and I use it quite a lot.
Besides its use as a desktop application it is not widely recognized that it can also be used as a backend document processing system by your Java application. Use cases are numerous: an invocice generating webapp in your intranet, prefilled document templates with content from a database or just an online inventory of your documents wiht support for on demand conversion to Microsoft Office formats or PDF.
The key to make your Java applications talk to OpenOffice is called UNO. UNO stands for Universal Network Objects and is the remoting system used by OpenOffice. It's a bit like CORBA.
First to enable remoting you must start OpenOffice with the -accept option:
/usr/bin/ooffice -accept="socket,host=0,port=8200,tcpNoDelay=1;urp;" -nologo
| Parameter | Description |
|---|---|
| host | Hostname or IP number of the resource to listen on/connect. May be localhost. In an acceptor string, this may be 0 ('host=0'), which means, that it accepts on all available network interfaces. |
| port | TCP/IP port number to listen on/connect to. |
| tcpNoDelay | Corresponds to the socket option tcpNoDelay. For a UNO connection, this parameter should be set to 1 (this is NOT the default ― it must be added explicitly). If the default is used (0), it may come to 200 ms delays at certain call combinations. |
Source: Professional UNO
You can also add the -headless option which allows using the application without user interface.
There is not much introducory documentation besides the heavy-weight Developers Guide and a blog entry from 2005.
You need a few jars that you can either download as part of the SDK from the OpenOffice.org website. If you are using Maven you can instead just add the following dependencies to your pom.xml:
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>juh</artifactId>
<version>2.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>jurt</artifactId>
<version>2.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>ridl</artifactId>
<version>2.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>unoil</artifactId>
<version>2.1.0</version>
<scope>compile</scope>
</dependency>
You need some boilerplate code to connect to OpenOffice:
Object x;
Object defaultContext;
Object desktop;
XComponentContext componentContext = Bootstrap.createInitialComponentContext(null);
x = componentContext.getServiceManager().createInstanceWithContext("com.sun.star.connection.Connector", componentContext);
XConnector connector = (XConnector) UnoRuntime.queryInterface(XConnector.class, x);
XConnection connection = connector.connect(getConnectionString());
x = componentContext.getServiceManager().createInstanceWithContext("com.sun.star.bridge.BridgeFactory", componentContext);
XBridgeFactory bridgeFactory = (XBridgeFactory) UnoRuntime.queryInterface(XBridgeFactory.class, x);
bridge = bridgeFactory.createBridge("", "urp", connection, null);
x = bridge.getInstance("StarOffice.ServiceManager");
XMultiComponentFactory multiComponentFactory = (XMultiComponentFactory) UnoRuntime.queryInterface(XMultiComponentFactory.class, x);
XPropertySet properySet = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, multiComponentFactory);
defaultContext = properySet.getPropertyValue("DefaultContext");
componentContext = (XComponentContext) UnoRuntime.queryInterface(XComponentContext.class, defaultContext);
multiComponentFactory = componentContext.getServiceManager();
desktop = multiComponentFactory.createInstanceWithContext("com.sun.star.frame.Desktop", componentContext);
componentLoader = (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class, desktop);
multiServiceFactory = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, multiComponentFactory);
Now you have a XComponentLoader that you can use to load additional resources like a document:
// Load the document, which will be displayed.
XComponent xComponent = openOfficeConnection.getComponentLoader().loadComponentFromURL(
"test.odt", "_blank", 0, new PropertyValue[0]);
// Get the textdocument
XTextDocument aTextDocument = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, xComponent);
If you didn't run OpenOffice in headless mode you'll see the document on your screen now. Let's have a look at some simple operations on that document:
XTextDocument xtd = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, xComponent);
XDocumentInfoSupplier xdis = (XDocumentInfoSupplier) UnoRuntime.queryInterface(XDocumentInfoSupplier.class, xtd);
XDocumentInfo xdi = xdis.getDocumentInfo();
XPropertySet xps = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, xdi);
XPropertySetInfo xpsi = xps.getPropertySetInfo();
XFastPropertySet xfps = (XFastPropertySet) UnoRuntime.queryInterface(XFastPropertySet.class, xps);
for (Property property : xpsi.getProperties())
{
String name = property.Name;
Object value = xfps.getFastPropertyValue(property.Handle);
System.out.println(name + ": " + value);
}
This code snippet prints the meta information of the document (what you get if you choose File/Properties...) to stdout.
Of course you can do much more, querying for tables in the document, changing or adding text, saving the document to different formats like Microsoft Word or PDF. Have a look at the javadocs and the IDL reference at http://api.openoffice.org/ and play with it.
Re: Using OpenOffice.org from Java Applications
Do you have an example (via ANT) how to compile it properly?
Thanks,
D.
Re: Using OpenOffice.org from Java Applications
I am using the OpenOffice.org jars in a web environment and it works well. I am sorry I don't have any ant scripts as I am using maven to build my projects but it should work much the same way with ant I suppose.
Re: Using OpenOffice.org from Java Applications
I don't know why OOO doesn't provide a lighter weight API for integrators. Loading a separate remoting engine is a pretty large concession just to support their file format. Compare to how lightweight Jexcel is for .xls and Itext for .pdf.
Jdom is so easy to use that I'm considering using the strategy above to create .xsc docs, then use UNO to convert to .xls. Trade-off between efficient performance with Jexcel lib vs. easy coding to just do the conversion with UNI.
Re: Using OpenOffice.org from Java Applications
There is a project that aims at implementing a pure Java library to work with OpenOffice document: odf4j
I didn't have a look at it yet, but it might be worth a look before starting from scratch.