Java - отчет с помощью OpenOffice.org Writer

OpenOffice.org

Напечатано под названием "Построй свой отчет": журнал "Системный администратор" №4, 2013г.
Генерация отчетов и в 21 веке остается одной из самых востребованных в информационных технологиях задач. Ничего удивительного, ведь для большинства предприятий бумажный документооборот остается пока основой делопроизводства. Вместе с тем, прошедшее десятилетие ознаменовалось бурным развитием свободного программного обеспечения (СПО). И действительно, в настоящее время можно построить информационную систему предприятия или часть ее на продуктах со свободной лицензией. Следуя духу СПО удачным опытом такого внедрения можно и нужно поделиться. В настоящей статье мы рассмотрим один из вариантов построения отчетов — с помощью свободно распространяемого офисного пакета OpenOffice.org (кратко OOo), а точнее — текстового редактора Writer, входящего в его состав.

ВЫБОР — СВОБОДНЫЕ ПРОГРАММЫ!

В одном из номеров журнала «Системный администратор» я натолкнулся на статью [1], которая позволила решить одну техническую проблему. Суть проблемы была в том, что нужен был простой и, желательно, бесплатный генератор отчетов для Java. Ну, не устраивали наше предприятие формы документов, идущие в поставке с программой на которой было построено делопроизводство. А разработчик программы не очень хотел что-то в ней переделывать.

К тому времени уже сложилась реализация доступа к БД с использованием Java, и было желание решить проблему своими силами. А почему бы и нет? Оставалось только выбрать технологию для реализации поставленной задачи.

В сторону коммерческих генераторов отчетов смотреть явно не хотелось. На сайтах их создателей и продавцов все выглядело красиво, но их стоимость, при всех достоинствах — таких как техническая поддержка, удобство использования, интеграция с различными средами разработки, была бы точно непонятна для руководства предприятия, расположенного достаточно далеко от столиц, судите сами (см. Таблицу 1):

Таблица 1. Цены на некоторые коммерческие генераторы отчетов для Java

Название Фирма-производитель(сайт) Стоимость 1 лицензии
i-net Clear Reports i-net (www.inetsoftware.de) $1690
StimulsoftReport.Fx for Java Stimulsotf (www.stimulsoft.com) $699,95
CrystalReports Business Object (www.crystalreports.com/) От $495

Поэтому решено было поискать что-то менее «болезненное» для бюджета предприятия. Напомню, что в [1] (материал назывался «Генерация отчетов по текстовым шаблонам»), в списке литературы значилась ссылка на статью А.Астафьева «Java + OpenOffice.org = генератор отчетов» (к сожалению, статья была удалена с ресурса http://i-rs.ru, однако, найти ее в Интернет с помощью Google не составит особого труда). Обе статьи рассматривают технологию генерации отчетов «по шаблону». Что в нашем случае было не лишено смысла, так как требовалось сформировать первичные документы для клиента, а при выписке счета, например, нужно просто расставить необходимые поля из базы данных в определенных местах формы (шаблона).

Кандидатом на победу при выборе генератора отчетов был еще один представитель СПО - библиотека Jasper Reports, использующая для разметки шаблона xml-файлы. Попытки протестировать Jasper Reports дали неплохие результаты. Эта библиотека хорошо документирована, в том числе есть материалы на русском языке [2], умеет сохранять результаты в разнообразных форматах: pdf, html, odt. Однако, не умаляя достоинств Jasper Reports и решения, описанного в [1], скажем, что использовать известный офисный пакет для разработки шаблонов, оказалось намного быстрее и удобнее. Кстати, можно озадачить созданием формы шаблона заинтересованного партнера (коллегу), занявшись вплотную вопросами программирования.

Собственно, мне и хотелось бы поделиться с читателями некоторыми идеями и блоками Java-кода, используя которые можно построить систему печати отчетов над существующей АИС, АРМ, СУБД и тому подобное.

ПОДКЛЮЧАЕМСЯ К БАЗЕ ДАННЫХ

Первой задачей, которую приходится решать при генерации отчетов, это, конечно, подключение к базе данных для получения необходимой информации. Рассмотрим, для примера, подключение к базе MS SQL (именно она была использована на нашем предприятии). Найти подходящий драйвер в сети не составит труда — на MSDN [3] есть и драйвер и соответствующая документация. Нам остается лишь подключить нужные jar-файлы к CLASSPATH. Для ОС семейства Linux это может выглядеть так:

Листинг 1. Файл .profile домашнего каталога пользователя (ОС Ubuntu 11.04)
export MS_SQL_HOME=/usr/local/java/ms
export MS_SQL_CLASSES=$MS_SQL_HOME/msbase.jar:$MS_SQL_HOME/mssqlserver.jar:$MS_SQL_HOME/msutil.jar
export CLASSPATH=.:$MS_SQL_CLASSES

Где MS_SQL_HOME — каталог, куда мы разместим файлы драйвера. В остальном подключение к базе данных не отличается от обычного использования JDBC (англ. Java DataBase Connectivity), ставшего для Java стандартом. Хотя, связь с данными с помощью JDBC для технологии отчетов по шаблону, которую мы описываем, вариант не единственный. Можно использовать JDO, Hibernate, или, например, библиотеку Castor (см. [4], [5]).

ПОДКЛЮЧАЕМ НЕОБХОДИМЫЕ БИБЛИОТЕКИ OpenOffice.org
Если подключение к базе данных можно рассматривать как немаловажную подготовку, то ядром нашего генератора отчетов станет пакет OpenOffice.org. Обратим внимание на подключение модулей, необходимых для работы API OpenOffice.org в CLASSPATH. Во-первых, кроме самого OpenOffice.org, нам необходимо установить OOo SDK (см. [6]). Для системы Ubuntu 11.04, например, это можно сделать командой:

$sudo apt-get install openoffice.org-dev

Теперь для нормального функционирования API OOo к CLASSPATH необходимо подключить следующие пакеты juh.jar, jurt.jar, ridl.jar, unoil.jar, а также отдельно путь к классам OOo SDK, что в Ubuntu после установки SDK выглядит так (см. Листинг 2):

Листинг 2. Дополнение к файлу .profile из Листинга 1
export OOO_HOME=/usr/share/java/openoffice
export CLASSPATH=$CLASSPATH:$OOO_HOME:$OOO_HOME/juh.jar:$OOO_HOME/jurt.jar:$OOO_HOME/ridl.jar:$OOO_HOME/unoil.jar:$OOO_HOME

После определения CLASSPATH программа компилируется обычным образом, а вот запуск уже откомпилированной программы должен производиться с помощью примерно такой командной строки:

$java -Dcom.star.lib.loader.unopath="/usr/lib/openoffice/program" com.sun.star.lib.loader.Loader ooo.my.program
где /usr/lib/openoffice/program - путь к исполняемым программным файлам OOo. Класс com.sun.star.lib.loader.Loader расположен в каталоге /usr/share/java/openoffice - поэтому этот путь нужно подключить к CLASSPATH.

ИСПОЛЬЗУЕМ ПОЛЯ OpenOffice.org

В статье А.Астафьева (подробности работы с API можно также посмотреть в Wiki на сайте Openoffice.org [7]) был приведен Java-класс, реализующий соединение с экземпляром OpenOffice, получение контекста удаленного компонента, сервиса рабочего стола, а если отбросить замысловатые названия, то — доступ к полям документа. Поля OpenOffice — это особые области документа, к которым можно обратиться по именам прямо из нашей Java-программы. Создать поле можно в любом месте шаблона документа из меню Вставка->Поля->Дополнительно программы Writer, а сохранить документ необходимо в формате odt. Обращение к полям в программе удобно реализовать с помощью контейнера HashMap, как показано в Листинге 2.

Листинг 2. Пример формирования контейнера с наименованием полей документа OpenOffice и их значений

HashMap<String,String> vMap = new HashMap<String,String>();
vMap.put("INVOICE_NUMBER", bd.getAccountNumber() );
vMap.put("INVOICE_DATE", bd.getAccountDate() );
vMap.put("CUSTOMER", bd.getCustomerName() );
vMap.put("CUSTOMER_ADDS", bd.getCustomerAddress() );

Где первый параметр метода put — название поля документа, а второй — некоторый метод, возвращающий строковое значение поля из базы данных.

Kод из Листинга 3 заполнит необходимые поля в документе:

Листинг 3. Пример заполнения полей документа с помощью экземпляра класса HashMap (vMap):

while ( xTextFieldsEnumeration.hasMoreElements() ) {
 Object service = xTextFieldsEnumeration.nextElement();
 XServiceInfo xServiceInfo = (XServiceInfo)UnoRuntime.queryInterface(XServiceInfo.class, service);
 if (xServiceInfo.supportsService ("com.sun.star.text.TextField.SetExpression")) {
   XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, service);
   String name = (String)xPropertySet.getPropertyValue("VariableName");
   Object content = vMap.get(name);
   xPropertySet.setPropertyValue("SubType", new Short(com.sun.star.text.SetVariableType.STRING));
   xPropertySet.setPropertyValue("Content", content == null ? " " : content.toString());
   xPropertySet.setPropertyValue("IsVisible", true);
 }
}

После того как мы откроем документ и заполним его поля, можно его напечатать и закрыть, см. примеры в Листинге 4:

Листинг 4. Примеры кода для соединения с экземпляром OpenOffice открытия документа, печати, закрытия с помощью API OpenOffice.org

// соединение с экземпляром OpenOffice.org
public void connect() throws Exception {
 // получение контекста удаленного компонента офиса
 xRemoteContext = com.sun.star.comp.helper.Bootstrap.bootstrap();
 xRemoteServiceManager = xRemoteContext.getServiceManager();
 // получение сервиса рабочего стола
 Object desktop = (XInterface)xRemoteServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", xRemoteContext);
 xDesktop = (XDesktop)UnoRuntime.queryInterface(XDesktop.class, desktop);

 //интерфейс для загрузки документа
 xComponentLoader = (XComponentLoader)UnoRuntime.queryInterface(XComponentLoader.class, desktop);
}

// Открытие документа, на который указывает sURL
public XComponent openDocument(String sURL) throws Exception {
 // преобразуем путь к файлу в URL
 java.io.File sourceFile = new java.io.File(sURL);
 StringBuffer sTmp = new StringBuffer("file:///");
 sTmp.append(sourceFile.getCanonicalPath().replace('\\', '/'));
 sURL = sTmp.toString();
 PropertyValue[] loadProps = new PropertyValue[0];
 return xComponentLoader.loadComponentFromURL(sURL, "_blank", 0, loadProps);
}

//печать документа
public void printDocument(XComponent comp) throws com.sun.star.lang.IllegalArgumentException {
 XPrintable xPrintable = (XPrintable) UnoRuntime.queryInterface(XPrintable.class, comp);
 PropertyValue[] printOpts = new PropertyValue[0];
 xPrintable.print(printOpts);
}

//Закрытие документа
closeDocument(currentDocument, false);
XCloseable c = (XCloseable)UnoRuntime.queryInterface(XCloseable.class, currentDocument);
while(true) {
 try {
 c.close(false);
 break;
 } catch (com.sun.star.util.CloseVetoException e) {
 }
}

Заметим, что API OpenOffice базируется на компонентной модели UNO (Universal Network Objects), распространяемой под лицензией LGPL. Многие программы, выгружающие свои отчеты в Microsoft Word/Excel используют для этого подобные технологии COM/DCOM. UnoRuntime - центральный класс необходимый для использования UNO компонентов в Java. Вот описание некоторых модулей связанных с технологией UNO, взятое из Apache OpenOffice.org Man [8] (см. Таблицу 2).

Таблица 2. Описание модулей, связанных с java UNO

jut - Java UNO Tools Инструменты для компонентов UNO
juh - Java UNO Helper Реализует преобразование и дает доступ к нативным (зависимым от платформы) компонентам
jurt - Java UNO Runtime Ядро java UNO
ridl - Java Runtime Interface Definition Library unoil - UNO Interface Library Набор интерфейсов для API OOo

Именно эти модули мы и пытались подключить в начале работы.

МНОГОСТРОЧНЫЕ ТАБЛИЦЫ В ДОКУМЕНТАХ

С чем можно еще столкнуться при формировании отчетов с помощью шаблонов OpenOffice? Обычно мы не можем заранее знать сколько позиций понадобится в шаблоне при выгрузке данных из базы, например, строк счета. Это можно решить сформировав несколько шаблонов с различным количеством строк и, соответственно, полей документа. Зная количество записей в ResultSet, которое возвратит SQL-запрос, можно поступить как описано в Листингах 5 и 6.

Листинг 5. Формирование полей OpenOffice в i-ой строке счета

for ( i=1; i =< k; i++) {
vMap.put("GOODS_NAME_" + i.toString(), rs.getString(1) );
vMap.put("QTY_" + i.toString(), rs.getString(2) );
vMap.put("PRICE_" + i.toString(), rs.getString(3) );
}

Листинг 6. Подмена шаблона документа в зависимости от количества строк (k)

switch (k) {
        case 1: TemplName="templ1.odt";
        case 2: TemplName="templ2.odt";
        case 3: TemplName="templ3.odt";
        case 4: TemplName="templ4.odt";
        case 5: TemplName="templ5.odt";
        default: TemplName="templ1.odt";
}

УПРАВЛЯЕМ ПЕЧАТЬЮ И ЭКОНОМИМ

Используя такую надстройку для генерации отчетов над существующей информационной системой, можно получить заметное преимущество в скорости и удобстве печати. Например, нетрудно сделать так, чтобы была возможность распечатывать несколько документов подряд, отметив нужные в простой экранной форме, как показано на Рис.1, облегчив тем самым труд оператора и увеличив его производительность.

Форма для выбора нужных документов при печати
Рисунок 1. Экранная форма для выбора нужных документов при печати

Отмечу, что генерация отчетов на базе ООо эксплуатируется на нашем предприятии с марта 2011 года на операционных системах семейств Linux и Windows. Вдобавок ко всему, можно отметить, что в существующей экономической ситуации полезно предоставить руководству отчет об эффекте от внедрения подобной системы. В зависимости от ситуации выгода может складываться из экономии на рабочих станциях с ОС Linux и/или OpenOffice, а также из стоимости коммерческого ПО для построения отчетов.

Таким образом, рассмотренный нами метод печати документов по шаблону на базе Apache OpenOffice.org Writer позволяет создать вполне «боеспособную» систему печати для документооборота предприятия. При этом есть возможность экономии на покупке как генератора отчетов, так и операционных систем и офисных программ.

В данной статье рассматривается как создать многостраничный отчет с помощью Java и технологии Apache POI.

1. А. Вторников Генерация отчетов по текстовым шаблонам. // «Системный администратор» №96, 2010г. с.82-86
2. http://voituk.kiev.ua/intro-jasper-reports - цикл статей о JasperReports2
3. http://msdn.microsoft.com/en-us/sqlserver/aa937724.aspx - Microsoft JDBC Driver For SQL Server
4. Ильичев С. Задачи сравнения списков или еще раз про XML. // «Системный администратор» №122-123, 2013г. с.86-90
5. http://www.ibm.com/developerworks/ru/library/x-xjavacastor4/ - пример использования библиотеки Castor для подключения к базе данных
6. http://www.openoffice.org/api/SDK/index.html - OpenOffice.org Software Development Kit (SDK)
7. http://wiki.openoffice.org/wiki/API/Samples/Java - Wiki Apache OpenOffice.org
8. http://www.openoffice.org/udk/java/man/ - Java in UNO and OpenOffice

Комментарии

Интересная особенность, при просмотре документа с полями в местах, где должны быть данные, появляется надпись "ИМЯ_ПОЛЯ" вставить значение = "...". Убирается это так. Заходим в настройки Open Office: Сервис - Параметры - OpenOffice.org Writer - Вид и убираем галочку из группы "Показывать" с параметра "Коды полей"

Страницы