Runtime Introspection of OpenOffice OXT package

The OpenOffice.org Developer’s Guide defines A OpenOffice.org OXT Extension as follows :

An extension is a file intended for the distribution of code and / or data which is to be used by OpenOffice.org. The file has the file extension (formerly .uno.pkg and .zip), and it acts as a container for various items, such as libraries, JARs, configuration data, type libraries, Basic libraries, Basic dialogs, etc. Before OpenOffice.org can use any content of the extension, it needs to be installed by the Extension Manager.
An extension should also contain a description.xml and must contain a directory META-INF (all uppercase). The META-INF directory contains a manifest.xml which lists all items and their media-type.

Recently i was working on a Openoffice.org Addon in Java and at some point of the project i needed a way to extract -at runtime- some information stored in the OXT binary (in fact I was looking for the OXT Properties).
If you’re using Netbeans openoffice.org plugin, you can get the OOo Extension Properties by right-clicking your project in the projects list and selecting OXT Extension properties, it’s under the Extension Management tab.
Because I needed to dynamically query my OXT extension for some useful information stored in the “description.xml” file , I decided to write a Java class to browse and parse the OXT Extension Files and extract OXT Properties at Runtime.

The most important fields that this class must knows about are the Extension Identifier and the com.sun.start.XComponentContext Object. So it makes good sense to pass these two Objects as arguments to class constructor.

/**
* constructor
* @param m_xContext
* @param extensionIdentifier
*/
public XPackageInformationParser(XComponentContext m_xContext, String extensionIdentifier) {
this.m_xContext = m_xContext;
this.extensionIdentifier = extensionIdentifier;
// some other initialization code goes here
}

OpenOffice UNO API provides a class com.sun.star.deployment.XPackageInformationProvider which is the entry class to access the OXT Package Information at runtime.
The following method configures and initialize the important UNO Objects that will be used to extract information from the UNO package:

private void intitialize(){
try {
XPackageInformationProvider xPackageInformationProvider = PackageInformationProvider.get(m_xContext);
oxtLocation = xPackageInformationProvider.getPackageLocation(this.extensionIdentifier);
Object oTransformer = m_xContext.getServiceManager().createInstanceWithContext("com.sun.star.util.URLTransformer", m_xContext);
xTransformer = (XURLTransformer) UnoRuntime.queryInterface(XURLTransformer.class, oTransformer);
} catch (Exception ex) {
_logger.error(ex);
}
}

this method calculates the OXT Package location path and stores it in a String variable OXTLocation. It also create a XURLTransformer Object that will be used to parse openoffice URLs.

To read all files contained within the OXT Extension Package I use the following method:

/**
* returns files list that are contained within current .oxt package at runtime
* @param dir directory to search files list (null for top level OXT directory)
* @return array of OXT files
*/
public String[] getFileList(String dir) {
com.sun.star.util.URL[] oURL = new com.sun.star.util.URL[1];
oURL[0] = new com.sun.star.util.URL();
oURL[0].Complete = (dir == null) ? oxtLocation + "/" : oxtLocation + "/" + dir;
xTransformer.parseStrict(oURL);
return new File(oURL[0].Path + oURL[0].Name).list();
}

By passing a null argument to this method it will return an array of all file names at top level of the OXT Package hierarchy structure.
It’s also possible to get a Java File Object reference to a given file contained within the OXT Extension Package. The below method do this :

/**
* get file within OXT package
* @param filePath
* @return reference to OXT File
*/
public File getExtensionFile(String filePath) {
com.sun.star.util.URL[] oURL = new com.sun.star.util.URL[1];
oURL[0] = new com.sun.star.util.URL();
oURL[0].Complete = this.oxtLocation + "/" + filePath;
xTransformer.parseStrict(oURL);
return new File(oURL[0].Path + oURL[0].Name);
}

To get a reference to the Java File Object of description.xml file located at top level of the OXT package , we call the method like this :

getExtensionFile(description.xml);

Now lets investigate the heart functionality of this class; which is the method that parses the OXT description.xml file and extract the OXT Properties and stores them in a Java Properties Object. The method is listed below:

/**
* parse OXT description file
*/
public void parseOXTProperties() {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// For HTML, we don't want to validate without a DTD
dbf.setValidating(false);
// Ignore text elements that are completely empty:
dbf.setIgnoringElementContentWhitespace(false);
dbf.setExpandEntityReferences(true);
dbf.setCoalescing(true);
// Ensure that getLocalName() returns the HTML element name
dbf.setNamespaceAware(true);
DocumentBuilder db = null;
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException pce) {
pce.printStackTrace();
return;
}
Document document = null;
File description = this.getExtensionFile(EXTENSION_DESC_FILE);
_logger.debug("got OXT description file: " + description.getAbsolutePath());
try {
document = db.parse(description);
NodeList nodes = document.getDocumentElement().getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node_i = nodes.item(i);
if (node_i.getNodeType() == Node.ELEMENT_NODE) {
Element element = ((Element) node_i);
if (element.getTagName().equals(VERSION)) {
this.properties.setProperty(VERSION, element.getAttribute(VALUE));
}
if (element.getTagName().equals(IDENTIFIER)) {
this.properties.setProperty(IDENTIFIER, element.getAttribute(VALUE));
}
if (element.getTagName().equals(DISPLAY_NAME)) {
NodeList nodeList = element.getElementsByTagName("name");
_logger.debug(nodeList.item(0).getNodeName());
this.properties.setProperty(DISPLAY_NAME, nodeList.item(0).getTextContent());
}
if (element.getTagName().equals(PUBLISHER)) {
NodeList nameNode = element.getElementsByTagName("name");
_logger.debug(nameNode.item(0).getNodeName());
this.properties.setProperty(PUBLISHER, nameNode.item(0).getTextContent());
}
}
}
} catch (SAXException ex) {
_logger.error(ex);
} catch (IOException ex) {
_logger.error(ex);
}
}

This method simply uses a java DOM parser to parse file description.xml ( file resides by-default at top level of OXT package) .It extracts useful xml document’s data (such as “publisher”, “version”, “display-name” etc..) then stores them in a Java Properties Object.
(the complete listing of XPackageInformationParser class is down-loadable from resources section at top bottom of this article.)

A Test code for printing OXT Package properties at runtime would look like :

XPackageInformationParser oxtParser=new XPackageInformationParser(m_xContext,org.openoffice.example);
System.out.println(oxtParser.getProperties());

you just replace string org.openoffice.example with your actual OOo Extension identifier (which you defined while creating your Addon component).

A sample output obtained by running above test code for my OXT Extension gives me the following Properties :

{display-name=OpenOffice.org Addon for SugarCRM , version=0.0.1, identifier=com.sun.star.addon.sugarcrm.OOoSugarAddOn, publisher=Othman El Moulat}

Now Of course you can do more useful things with this class by examining each file retrieved from OXT Package at runtime. for example the /META-INF/manifest.xml file contains some useful information that could be used at runtime by your Java OOo Add-on.

Below is the complete listing of the XPackageInformationParser class.



import com.sun.star.deployment.PackageInformationProvider;
import com.sun.star.deployment.XPackageInformationProvider;
import com.sun.star.uno.Exception;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.util.XURLTransformer;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
* utility class to parse a OXT extension package.
* @author othman
*/
public class XPackageInformationParser {

private static Logger _logger = Logger.getLogger(XPackageInformationParser.class);
private static final String EXTENSION_IDENTIFIER = "com.sun.star.addon.sugarcrm.OOoSugarAddOn";
private static final String EXTENSION_DESC_FILE = "description.xml";
private static final String VERSION = "version";
private static final String PUBLISHER = "publisher";
private static final String IDENTIFIER = "identifier";
private static final String DISPLAY_NAME = "display-name";
private static final String VALUE = "value";
private XComponentContext m_xContext;
private XURLTransformer xTransformer;
private String extensionIdentifier;
private String OXTLocation;
private Properties properties;

/**
*constructor
* @param m_xContext
*/
public XPackageInformationParser(XComponentContext m_xContext) {
this(m_xContext, EXTENSION_IDENTIFIER);
}

/**
* constructor
* @param m_xContext
* @param extensionIdentifier
*/
public XPackageInformationParser(XComponentContext m_xContext, String extensionIdentifier) {
this.m_xContext = m_xContext;
this.extensionIdentifier = extensionIdentifier;
properties = new Properties();
this.intitialize();
this.parseOXTProperties();

}

private void intitialize() {
try {
XPackageInformationProvider xPackageInformationProvider = PackageInformationProvider.get(m_xContext);
OXTLocation = xPackageInformationProvider.getPackageLocation(this.extensionIdentifier);
Object oTransformer = m_xContext.getServiceManager().createInstanceWithContext("com.sun.star.util.URLTransformer", m_xContext);
xTransformer = (XURLTransformer) UnoRuntime.queryInterface(XURLTransformer.class, oTransformer);
} catch (Exception ex) {
_logger.error(ex);
}
}

/**
* returns files list that are contained within current .OXT package at runtime
* @param dir directory to search files list (null for top level OXT directory)
* @return array of OXT files
*/
public String[] getFileList(String dir) {

com.sun.star.util.URL[] oURL = new com.sun.star.util.URL[1];
oURL[0] = new com.sun.star.util.URL();
oURL[0].Complete = (dir == null) ? OXTLocation + "/" : OXTLocation + "/" + dir;
xTransformer.parseStrict(oURL);
return new File(oURL[0].Path + oURL[0].Name).list();
}

/**
* get file within OXT package
* @param filePath
* @return reference to OXT File
*/
public File getExtensionFile(String filePath) {

com.sun.star.util.URL[] oURL = new com.sun.star.util.URL[1];
oURL[0] = new com.sun.star.util.URL();
oURL[0].Complete = this.OXTLocation + "/" + filePath;
xTransformer.parseStrict(oURL);
return new File(oURL[0].Path + oURL[0].Name);

}

/**
* parse OXT description file
*/
public void parseOXTProperties() {

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

// For HTML, we don't want to validate without a DTD
dbf.setValidating(false);
// Ignore text elements that are completely empty:
dbf.setIgnoringElementContentWhitespace(false);
dbf.setExpandEntityReferences(true);
dbf.setCoalescing(true);

// Ensure that getLocalName() returns the HTML element name
dbf.setNamespaceAware(true);

DocumentBuilder db = null;
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException pce) {
pce.printStackTrace();
return;
}

Document document = null;

File description = this.getExtensionFile(EXTENSION_DESC_FILE);
_logger.debug("got OXT description file: " + description.getAbsolutePath());

try {
document = db.parse(description);
NodeList nodes = document.getDocumentElement().getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node_i = nodes.item(i);

if (node_i.getNodeType() == Node.ELEMENT_NODE) {
Element element = ((Element) node_i);

if (element.getTagName().equals(VERSION)) {

this.properties.setProperty(VERSION, element.getAttribute(VALUE));
}

if (element.getTagName().equals(IDENTIFIER)) {

this.properties.setProperty(IDENTIFIER, element.getAttribute(VALUE));
}

if (element.getTagName().equals(DISPLAY_NAME)) {

NodeList nodeList = element.getElementsByTagName("name");
_logger.debug(nodeList.item(0).getNodeName());

this.properties.setProperty(DISPLAY_NAME, nodeList.item(0).getTextContent());
}

if (element.getTagName().equals(PUBLISHER)) {

NodeList nameNode = element.getElementsByTagName("name");
_logger.debug(nameNode.item(0).getNodeName());

this.properties.setProperty(PUBLISHER, nameNode.item(0).getTextContent());
}

}
}
} catch (SAXException ex) {
_logger.error(ex);
} catch (IOException ex) {
_logger.error(ex);
}

}

/**
* checks if OXT description file exists.
*
* @return true if OXT description file exists
*/
public boolean ensureExtensionDescription() {
boolean exists = false;
String[] OXTFile = this.getFileList(null);
for (int i = 0; i < OXTFile.length; i++) {
if (OXTFile[i].equals(EXTENSION_DESC_FILE)) {
return true;
}
}
return exists;

}

public String getOxtLocation() {
return OXTLocation;
}

public Properties getProperties() {
return properties;
}

/**
*
* @return
*/
public String getVersion() {
return properties.getProperty(VERSION);
}

public String getPublisher() {
return properties.getProperty(PUBLISHER);
}

public String getIdentifier() {
return properties.getProperty(IDENTIFIER);
}

public String getDisplayName() {
return properties.getProperty(DISPLAY_NAME);
}
}

Resources:

Advertisements

One comment on “Runtime Introspection of OpenOffice OXT package

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s