вторник, 21 августа 2012 г.

Использование шрифтов в JasperReports

Не так давно одной из моих задач было создать модуль отчетности, позволяющий разным клиентским приложениям использовать его для генерации и отдачи сгенерированного контента. При реализации этой задачи я активно работал с библиотекой JasperReports 4.5.1. Одна из первоочередных задач при работе с кирилическими документами - подключение шрифтов. В стандартном варианте подключения JasperRepots не имеет поддержки UTF-8 шрифтов. В нашем случае были необходимы шрифты Arial и Helvetica. Ни того ни другого нет по умолчанию. Дальше я опишу два подхода. Первый был выбран на скорую руку и я впоследствии решил от него отказаться. Второй подход - то как надо было делать сразу и он верный.

Решение 1. - Неправильное(мое оценочное суждение).

  • Скачать файлы ttf, из интернета, либо, если у вас windows, то их можно найти в директории {WIN_INSTALLATION_DIR}\Fonts\
  • Положить эти файлы в директорию проекта, чтобы они были доступны в classpath. Например сюда {project_dir}\src\main\resources\Arial.ttf
  • Явно указать название файла в отчете.
Такой подход привел сразу к проблемам, выяснилось, что выделение жирным не работает, потому что указанный шрифт не содержит жирного варианта. Была выбрана заплатка в таком виде - явно указывать шрифт для элемента. Поскольку сроки поджимали так и было сделано: Конечно, это жутко неудобно не только потому, что так можно делать только для элемента, а внутри элемента разделение на жирный и нежирный шрифт работать не будет, но и как следствие мы лишаемся гибкости в комбинировании элементов и их расположении. Ну и плюс к тому нужно постоянно прописывать явно имя шрифта. Почитав внимательнее документацию по JasperReports был выбран и использован верный способ работы со шрифтами.

Решение 2 - Правильное(рекомендованное документацией и сообществом JasperReports)

Для этого решения используется расширение стандартных шрифтов JasperReports через конфигурационные файлы. Шаги следующие:

  • Скачать/скопировать необходимые шрифты в папку {project_dir}\src\main\resources\fonts
  • Создать файл jasperreports_extension.properties в директории {project_dir}\src\main\resources\. Файл приведен ниже:
  • Создать файл specialfonts.xml в директории {project_dir}\src\main\resources\fonts Файл содержит описание того откуда JasperRepots будет брать файлы шрифтов и какой файл какому(жирному, курсиву и т.д.) стилю соответствует. Ниже пример моего файла:
  • Указать какой шрифт будет использоваться в шаблоне отчета.

Плюс этого подхода в том, что в шаблоне указывается просто имя шрифта, весь маппинг и настройки находятся за пределами шаблона. Дополнительно мы получаем гибкость в использовании жирных курсивов и прочих вариантов текста внутри одного шрифта.

понедельник, 20 августа 2012 г.

JBOSS 7 and several persistence units.

В течение этого года один из проектов, в котором я участвую, пишется с использованием технологий JEE, EJB3, JPA. Проект интеграционный и зачастую возникает задача использования нескольких баз в рамках одного модуля. Решается это просто: используется несколько persistence-unit в рамках одного persistence.xml. Например вот так: Все бы ничего вот только проблема заключается в том, что при таком подходе при запуске JBOSS жалуется на ошибку инициализации CoreReportAnotherSchemaJPA контекста, он пытается в нем найти связку для класса ReportCategory, который вроде бы даже не определен в этом persistence-unit. Оказывается проблема заключается в следующем. При определении персистеных классов во всех модулях нашего проекта используется аннотация Entity. Ниже пример такого класса: При старте приложения, сервер приложений не обращает внимание на то, что явно указаны классы в persistence-unit он сканирует на предмет наличия аннотации Entity доступный в classpath классы. Отсюда возникает эта коллизия. Решение оказалось довольно простым в спецификации есть тег exclude-unlisted-classes по умолчанию он имеет значение false, поэтому и добавляются не нужные мне классы и происходит коллизия с тем что указано. Включив этот флажок получаем именно то что нам нужно - включаться будут только те классы что указаны руками. Конечно, в обычном приложении без интеграционных замарочек с legacy кодом в разных базах такой проблемы не возникнет. Но кому-то все же этот прием может понадобится. Ниже приведен пример работающего кода с означенным тегом:

среда, 26 января 2011 г.

Использование placeholders из одного properties файла для конфигурации другого.

Иногда появляются нетривиальные задачи конфигурирования приложения для определенных нужд. Один из примеров: есть приложение, которое должно работать в разных environment'ах: development, test, production. В зависимости от environment'а используются разные файлы конфигурации/настройки. Другой пример - одно и тоже приложение может запускаться в нескольких экземплярах в одном application-сервере, при этом по разному сконфигурировано, с помощью разных файлов настроек.
Во втором случае одно из достаточно неплохих решений я опишу ниже. Файлы конфигурации - это обычные property-файлы с парами ключ значение. При этом конфигуарция всех модулей находится в одном месте, путь к которому я указываю через переменную окружения CONF_HOME. Эта переменная указывает на корень конфигурации. А в нем уже идут директории module1, module2, ..., moduleN, module1-diffconfig, modulek-n-configuration. Внутри каждой папки есть файлы типа conf.properties.


В spring чтобы использовать значения из файлов настройки в виде ${server.property1} нужно объявить в context.xml следующую конструкцию:


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


Решение следующее: в каталоге CONF_HOME для каждого экземпляра модуля заводится отдельная папка, а в ней находятся файлы настройки с одинаковыми именами,но, понятно, с разными значениями одних и тех же параметров:



Понятно, что основной вопрос - как нам изменить строчку в context.xml

чтобы вместо module1 подставлялось значение для конкретного экземляпра модуля. Решений может быть несколько, один из простых вариантов - сделать дополнительный файл настроек module.properties в котором будет одно поле nodeName=module-specific-conf. Можно сделать таким образом, чтобы это значение подменялось при мавенской сборке. Поскольку это не тема данной статьи, то варианты решения я оставлю на усмотрение любознательного читателя.


Итак я сделал дополнительный файл настроек, который собирается в war-ник и его значение используется при поиске специфичных для приложения настроек.
Казалось бы теперь все очень просто и нам должна помочь вот такая конструкция:


Но ни тут-то было. Spring при инициализации бина, не может распознать nodeName. Оказывается это связано с некой спецификой при загрузке property-файлов и работы PropertyPlaceholderConfigurer-ов. На форуме Spring'а есть несколько тем и даже запросы в джире на изменения, но все мимо. На помощь приходит SpEL - Spring Expression Language, появившийся в Spring 3. С его помощью проблема решается просто и элегантно. Вот так:


Объяснения требует лишь одно выражение:


Здесь решетка означает, что дальше идет SpEL, а дальнейшее - это доступ к бину и его свойству. Вот все просто получилось, благодаря третьему Spring.

среда, 21 апреля 2010 г.

Wicket работа с Ajax

Рассмотрим как работает Wicket с Ajax. Для демонстрации рассмотрим следующий пример: при нажатии на гиперссылку, меняется содержимое другой области страницы.
Шаблон страницы:

Класс страницы:

Из кода видно, что при клике на гиперссылке, происходит ajax вызов метода onClick класса гиперссылки. Внутри этого метода обновляется значение поля totalAmountLabel. Чтобы обновление действительно произошло нужно вызвать target.addComponent(). Конечно, таким образом можно обновлять и поля формы, в зависимости от изменений других полей.

понедельник, 19 апреля 2010 г.

Wicket кастомизация html при рендеринге компонентов.


Зачастую при написании приложений на основе компонентных фреймворков, стандартный шаблон компонента требуется подправить. Например, поменять стиль, динамически вставить дополнительный элемент html разметки. В этой статье я разберу как это можно сделать в Wicket.

В базовом классе Component есть два метода для динамического добавления/изменения содержимого компонента:

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

С помощью данного метода можно заменять, добавлять атрибуты в тег компонента.

Рассмотрим случай, когда по какой-то причине мне нужно добавить атрибут class со специфичным значением в компонент AjaxFallbackLink.


Код довольно прост. В заивсимости от какого-то условия я добавляю (или меняю уже существующее значение) атрибута class у тега гиперссылки. Аналогичным образом можно удалить этот же атрибут. Для этого можно использовать следующий метод:

Теперь рассмотрим случай, когда ссылки добавляются динамически. Из предыдущей статьи известно что для этого используется вспомогательный компонент RepeatingView. Проблема в том, что компонент-ссылка содержится в WebMarkupContainer, а последний в RepeatingView. И сразу непонятно где именно нужно переопределять метод onComponentTag, чтобы добраться до самой ссылки. Опытным путем я пришел к тому, что переопределять его нужно так же в самой ссылке. Не знаю почему, но для меня это было неочевидно. Я перепробовал варианты с переопределением этого метода как для RepeatingView, так и для WebMarkupContainer. В итоге пришел к самой ссылке.

Рассмотрим пример с изменением содержимого тега компонента.


Сначала проверяю условие, если оно не выполняется, то тело тега остается без изменения. Если же условие выполняется, то формирую строку, на которую хочу заменить содержимое компонента, а потом вызываю метод заменяющий тело тега компонента.

Как видно из примеров Wicket дает довольно обширные возможности для манипуляций с контентом компонентов. И делать это довольно просто. Правда основной минус всех этих манипуляций состоит в том, что для каждого компонента, содержимое которого мы хотим поменять, нужно создавать анонимный подкласс, переопределяющий тот или иной метод. От этого код становится трудночитаемым и плохо сопровождаемым. Вообще я считаю "многословность" основной проблемой этого фреймворка.

четверг, 15 апреля 2010 г.

Wicket Работа с динамическими списками элементов

Теперь, когда я рассказал про общую концепцию, установку и валидацию в Wicket, перейдем к работе с его компонентами.


Задача. Необходимо отображать данные, количество которых меняется, то есть неизвестно заранее сколько их будет. Это может быть таблица товаров, перечень допустимых действий над объектом или что-то еще. Все это объединяет тот факт, что мы не знаем в момент написания шаблона, сколько табличных или списочных элементов нам понадобится.


Для этих целей используется компонент RepeatingView, он не отображается сам при отрисовке страницы. Его основное назначение - быть контейнером для динамических данных.


Сразу перейдем к примеру и его объяснению. Я буду отображать на странице некие статические данные (понятно, что здесь может быть вызов сервиса, предоставляющего данные из базы). Шаблон страницы (Example.html) выглядит так:



Шаблон довольно простой. Стоит заметить, что для всех элементов, содержащихся в interestLinksList будут повторяться только внутренние элементы. То есть span повторяться не будет.


Рассмотрим класс страницы.

Теперь разберем этот код более подробно. Класс ListItem вспомогательный. Он создан лишь для удобства работы со списками. Статическая константа LIST необходима тоже лишь для демонстрации, поскольку в обычном приложении будет использоваться поставщик данных из сервис слоя. Более интересно наличие промежуточного компонента WebMarkupContainer. Этот компонент необходим для того, чтобы Wicket мог отличить различные элементы динамического списка. Для этого разработчик создает этот промежуточный компонент и устанавливает ему id с помощью вызова

Замечу, что в Tapestry 5, этот процесс автоматизирован и скрыт от глаз разработчика, что, по моему мнению, является правильным шагом. Здесь же нужно это делать самому. Таким образом после выполнения программы мы получим страницу, содержащую html примерно такого вида:

Уникальные id я опустил. Главное, что нет никаких вспомогательных тегов и повторяющийся контент содержится внутри основного тега контейнера. RepeatingView точно также можно использовать и с повторяющимися элементами формы. Я покажу это в статьях, посвященных различной работе с формами.

среда, 14 апреля 2010 г.

Wicket - первое приложение.

Собственно с этой статьи и надо было начинать рассказ про Wicket, но лучше поздно чем никогда. Итак, я предполагаю использование maven. Cтруктура каталогов соответствующая.

1. Добавляем в pom.xml зависимость:

Сюда же можно добавить поддержку wicket-security, но об этом я расскажу в статьях, посвященных интеграции с этой подсистемой.

2. Создаем страницу Index.
Общее описание коцепций я приводил здесь. Ниже шаблон и класс страницы, а так же properties файл.
Шаблон

Класс страницы.

properties файл.

3. Создаем класс WicketApplication.
Это класс расширяет WebApplication, в котором находятся методы, управляющие различными свойствами Wicket приложения. Я в нем хочу переопределить то какую страницу Wicket будет отдавать как "домашнюю".

Класс WebApplication содержит много интересных методов. Я буду разбирать их в следующих статьях на тему Wicket.

4. Добавить WicketApplication в spring контекст.

5. Изменить web.xml.
Он должен иметь следующий вид:

Здесь требуется пояснение. Первая строка необходима, чтобы загрузить в контекст бин wicketApplication. SpringWebApplicationFactory - это реализация интерфейса IWebApplicationFactory, позволяющая получать WebApplication объект из контекста Spring.

Теперь после запуска приложения, мы всегда будем попадать на стартовую страницу.