Monday, July 14, 2008

Управление зависимостями при разработке UNO-компонентов для OpenOffice.org на Java

Дополнение к предыдущей статье "Создание Java-расширений для OpenOffice.org".

В прошлой статье я показал как создать простое расширение для OpenOffice.org на Java. Простое - потому что оно ничего не делало с интерфейсом пользователя и при его разработке не использовались функции и классы, реализованные в сторонних библиотеках (JAR'ах).

Практически ни одно более-менее серьезное приложение не обходится без использования сторонних библиотек, взять хотябы log4j, без которого не обходится ни один мой рабочий проект.

Следовательно, если вы используете сторонние библиотеки, вам нужно включить их в CLASSPATH для вашего расширения. Если этого не сделать, то при установке расширения возникнет стандартное java.lang.NoClassDefFoundError.

Есть несколько способов указать CLASSPATH для Java-приложений, но для OOo подходит только один - указать зависимости в манифесте JAR'а компонента. По крайней мере, по другому у меня не получилось.

Чтобы построить CLASSPATH, нужно прописать ссылки на все используемые JAR'ы в файле-манифесте. Но используя Maven2 вы можете облегчить себе эту работу, воспользовавшись plugin'ом Maven2 Assembly Plugin.

Этот plugin позволяет "готовить" различные сборки проекта по описанному файлу сборки. Среди преднастроенных дескрипторов сборки есть jar-with-dependencies. Этот дескриптор позволяет для проектов-библиотек (проекты со свойством packaging установленным в jar) сформировать на выходе один JAR, в котором будут упакованы все зависимые JAR'ы, включая классы и другие артефакты текущего проекта. Все помнят, что JAR - это обычный zip-архив? Так вот, сборка jar-with-dependencies распаковывает все зависимости и скомпилированные артефакты проекта во временную папку, а затем эту папку снова упаковывает в один JAR. А это как раз то, что нам нужно.

Чтобы подключить этот plugin в pom.xml в ветку project/build/plugins нужно прописать:

<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies
</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>attached</goal>
</goals>
</execution>
</executions>
</plugin>


Когда я собрал такой JAR и положил его в корень oxt-архива, мое расширение перестало устанавливаться. Процесс установки прерывался сообщением об ошибке: "Cannot determine registration class!".

Я так и не смог толком понять, почему в этом "большом" JAR'е OOo не мог найти мой регистрационный класс. Проблему удалось решить путем разделения "большого" JAR'а на два - в одном был код моего проекта, в другом - код зависимых JAR'ов. Для этого в процесс сборки проекта пришлось вставить ant:

<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>verify</phase>
<configuration>
<tasks>
<mkdir dir="target/oxt/skip_registration" />
<zip
destfile="target/oxt/skip_registration/${artifactId}-${version}-dependencies.jar">
<zipFileSet
src="target/${artifactId}-${version}-jar-with-dependencies.jar"
excludes="org/keyintegrity/ooo/**" />
</zip>
<zip destfile="target/${artifactId}-${version}.oxt">
<fileSet dir="." includes="README*, LICENSE*, NOTICE*" />
<fileSet dir="target" includes="${artifactId}-${version}.jar" />
<fileSet dir="target/oxt" includes="**/*" />
</zip>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>


Можно заметить, что в oxt-архиве JAR с зависимостями находится в папке skip_registration. OOo знает про эту папку и он не пытается зарегистрировать ее содержимое как компонент OOo.

Осталось добавить ссылку на этот JAR в CLASSPATH моего компонента и все снова заработало:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<configuration>
<archive>
<manifestEntries>
<RegistrationClassName> org.keyintegrity.ooo.ActivityMonitor
</RegistrationClassName>
<Class-Path>
skip_registration/${artifactId}-${version}-dependencies.jar
</Class-Path>
</manifestEntries>
</archive>
</configuration>
</plugin>