Бизнес-отчётность - это та область, где красивая обёртка имеет не меньшее значение, чем правильные цифры. Финансовый директор хочет видеть квартальные показатели на одном листе с графиками и диаграммами, маркетолог - аккуратно свёрстанный PDF с данными по кампаниям, бухгалтерия требует Excel-таблицы со сложным форматированием. Самостоятельно собирать всё это из голых данных в базе - занятие неблагодарное, особенно когда отчёты нужны регулярно и в нескольких форматах одновременно. Тут в дело вступают специализированные системы построения отчётов.

JasperReports - один из ветеранов жанра. Проект существует с 2001 года, написан на Java и эволюционировал из библиотеки для встраивания в приложения в полноценную серверную платформу с веб-интерфейсом, управлением пользователями, расписанием генерации отчётов и API для интеграции. Главная ценность инструмента - богатый язык описания шаблонов отчётов с поддержкой группировок, агрегаций, перекрёстных таблиц, графиков, штрих-кодов и сложного форматирования. Один и тот же шаблон может выдавать результат в HTML, PDF, XLS, RTF, CSV, XML, ODT и нескольких других форматах без изменений в исходнике.

В материале разбирается полный сценарий развёртывания JasperReports Server Community Edition на Ubuntu 22.04. Установка Java JDK как фундамента, развёртывание базы данных MariaDB для хранения метаданных и репозитория отчётов, настройка контейнера сервлетов Apache Tomcat с правильным systemd-юнитом, прогон автоматического инсталлятора JasperReports с настройкой security policy и финальное проксирование через Nginx для удобного доступа по доменному имени.

Установка комплекта разработки Java JDK как обязательной основы для запуска приложений на платформе JVM

JasperReports - это Java-приложение, и без подходящей среды выполнения оно никуда не поедет. На Ubuntu 22.04 в стандартных репозиториях лежит OpenJDK 11, который отлично подходит для актуальных версий JasperReports. Установка одной командой подтягивает всё необходимое.

apt install default-jdk unzip wget -y

Пакет default-jdk - это мета-пакет, который тянет за собой текущую рекомендуемую версию OpenJDK для Ubuntu. На 22.04 это OpenJDK 11. Параллельно ставятся unzip для распаковки ZIP-архивов с дистрибутивом JasperReports и wget для скачивания файлов с удалённых серверов. Флаг -y автоматически отвечает положительно на все запросы подтверждения.

Проверка успешной установки.

java --version

Вывод подтверждает наличие работающей JVM в системе.

openjdk 11.0.16 2022-07-19
OpenJDK Runtime Environment (build 11.0.16+8-post-Ubuntu-0ubuntu122.04)
OpenJDK 64-Bit Server VM (build 11.0.16+8-post-Ubuntu-0ubuntu122.04, mixed mode, sharing)

Версия 11.0.16 - это OpenJDK 11 с минорным обновлением. Цифры могут немного отличаться в зависимости от того, когда именно ставится система, потому что Ubuntu регулярно подтягивает security-обновления. Главное, чтобы в выводе фигурировала именно одиннадцатая версия.

Стоит понимать, почему Java остаётся такой важной платформой для энтерпрайз-приложений вроде JasperReports. JVM абстрагирует приложение от операционной системы, обеспечивает управляемую сборку мусора, предоставляет богатую стандартную библиотеку и огромную экосистему сторонних компонентов. Для серверных приложений с долгим временем работы и большим количеством параллельных пользователей это сочетание оказывается очень удобным. Отсюда и долгое доминирование Java в корпоративном сегменте, где надёжность важнее моды.

Развёртывание базы данных MariaDB и создание учётной записи для подключения JasperReports

JasperReports хранит свои метаданные в реляционной базе данных - описания подключений к источникам данных, шаблоны отчётов, расписания, права пользователей, история выполнения. Поддерживаются разные движки СУБД, но MariaDB остаётся одним из самых проверенных вариантов на Linux-серверах благодаря низкому потреблению ресурсов и простоте настройки.

apt install mariadb-server -y

После установки MariaDB автоматически стартует и прописывается в автозагрузку. Стандартная конфигурация Ubuntu сразу позволяет подключаться к серверу через unix-сокет от имени пользователя root без пароля - это удобно для первоначальной настройки.

mysql

Команда mysql на самом деле запускает MariaDB-клиент, потому что MariaDB сохраняет совместимость с инструментарием MySQL ради облегчения миграции с одного движка на другой. Внутри клиента создаётся отдельный пользователь для нужд JasperReports.

MariaDB [(none)]> grant all on *.* to master@localhost identified by 'password';

SQL-запрос делает несколько вещей за один проход. Создаётся пользователь master с паролем password, и ему сразу выдаются права на все базы данных и все таблицы (звёздочки в *.* - это wildcard). Условие @localhost ограничивает подключения только локальными - удалённые попытки залогиниться под этим именем будут отклоняться.

Пароль password в туториале оставлен в качестве примера - на боевой инсталляции его обязательно нужно заменить на длинную случайную последовательность. Учётная запись с правами на всё хозяйство СУБД - лакомая цель для атакующих, и слабый пароль может стать вектором компрометации не только JasperReports, но и любых других баз на этом же сервере.

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

MariaDB [(none)]> flush privileges;
MariaDB [(none)]> exit;

Команда flush privileges перезагружает таблицы привилегий MariaDB, чтобы новые правила сразу вступили в силу. Без неё некоторые изменения учёток применяются только при следующем перезапуске СУБД. Команда exit завершает сессию клиента.

Установка контейнера сервлетов Apache Tomcat и подготовка структуры каталогов под нужды веб-приложения

JasperReports Server - это Java EE веб-приложение, упакованное в WAR-файл. Чтобы его запустить, нужен контейнер сервлетов, и Apache Tomcat остаётся стандартным выбором для такого сценария. Tomcat лёгкий, шустрый, хорошо документированный и совместимый практически с любым Java-приложением, рассчитанным на стандартные сервлет-API.

Перед установкой создаются выделенные пользователь и группа для работы Tomcat. Запускать сетевые приложения от root - дурной тон, любая уязвимость в коде превращается в дыру с правами суперпользователя на всю систему.

groupadd tomcat
useradd -s /bin/bash -g tomcat -d /opt/tomcat tomcat

Команда groupadd создаёт группу с именем tomcat. Параметры useradd задают оболочку (/bin/bash), основную группу (-g tomcat), домашний каталог (-d /opt/tomcat) и имя пользователя. Размещение в /opt - стандартное место для сторонних приложений на Linux-серверах.

Создание самого каталога установки.

mkdir /opt/tomcat

Скачивание дистрибутива Tomcat 8 с официального зеркала.

wget https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.82/bin/apache-tomcat-8.5.82.tar.gz

Версия 8.5.82 - это поздний релиз ветки Tomcat 8. На свежих установках можно было бы поставить и Tomcat 9 или 10, но JasperReports официально поддерживает именно восьмую ветку, и от добра добра не ищут. Совместимость и стабильность важнее возможности похвастаться последней цифрой версии.

Распаковка архива в каталог установки.

tar -xzvf apache-tomcat-8.5.82.tar.gz -C /opt/tomcat --strip-components=1

Флаги tar расшифровываются как extract (-x), gzip (-z), verbose (-v) и file (-f). Параметр -C указывает целевой каталог для распаковки, а --strip-components=1 пропускает первый уровень вложенности в архиве - без него все файлы Tomcat оказались бы в /opt/tomcat/apache-tomcat-8.5.82/, а нам нужно, чтобы они лежали прямо в /opt/tomcat/.

Назначение правильного владельца на распакованные файлы.

chown -R tomcat: /opt/tomcat
sh -c 'chmod +x /opt/tomcat/bin/*.sh'

Команда chown с флагом -R рекурсивно меняет владельца у всех файлов и подкаталогов. Двоеточие после tomcat без указания группы означает использование основной группы пользователя tomcat. Затем chmod ставит флаг executable на все скрипты в каталоге bin - там лежат startup.sh, shutdown.sh, catalina.sh и прочие управляющие команды, которые без права на выполнение работать не будут.

Настройка systemd-юнита для управления сервисом Tomcat и обеспечения автозапуска при загрузке системы

Запускать Tomcat руками через startup.sh - решение для разработки и тестов. На боевом сервере правильнее оформить запуск как systemd-сервис, чтобы получить автозапуск при загрузке, автоматический перезапуск при сбоях и централизованное логирование через journalctl.

nano /etc/systemd/system/tomcat.service

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

[Unit]
Description=Tomcat webs servlet container
After=network.target

[Service]
Type=forking

User=tomcat
Group=tomcat
RestartSec=10
Restart=always 
Environment="JAVA_HOME=/usr/lib/jvm/java-1.11.0-openjdk-amd64"
Environment="JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom"

Environment="CATALINA_BASE=/opt/tomcat"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_PID=/opt/tomcat/temp/tomcat.pid"
Environment="CATALINA_OPTS=-Xms1024M -Xmx2048M -server -XX:+UseParallelGC"

ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh

[Install]
WantedBy=multi-user.target

Содержимое заслуживает построчного разбора, потому что каждая переменная окружения имеет конкретное значение.

Секция Unit задаёт описание сервиса и указывает запускаться после поднятия сети - без этого Tomcat мог бы стартовать раньше, чем сетевая подсистема, и упасть с ошибкой привязки к портам.

Тип forking в секции Service означает, что главный процесс startup.sh форкает реальный JVM-процесс Tomcat и завершается. Systemd должен это понимать, чтобы правильно отслеживать состояние сервиса. Параметры User и Group явно задают учётную запись для запуска - именно тот пользователь tomcat, которого создавали ранее.

Параметр Restart=always перезапускает Tomcat при любом завершении, RestartSec=10 ставит десятисекундную паузу между падением и попыткой нового запуска. Без этого тайм-аута получился бы бесконечный цикл рестартов при систематической ошибке.

JAVA_HOME указывает корневой каталог JDK. Путь /usr/lib/jvm/java-1.11.0-openjdk-amd64 соответствует стандартному размещению OpenJDK 11 в Ubuntu - если используется другая версия, путь нужно скорректировать.

JAVA_OPTS содержит две важные опции запуска JVM. Параметр headless=true говорит Java, что графической подсистемы нет и не будет - это нормальное состояние для серверного окружения. Без него некоторые операции с графикой в JasperReports могут падать с ошибкой попытки открыть X-сервер. Вторая опция указывает источник энтропии для генерации случайных чисел - использование /dev/urandom вместо /dev/random ускоряет старт приложения, потому что не приходится ждать накопления реальной энтропии.

CATALINA_BASE и CATALINA_HOME указывают пути к экземпляру Tomcat. В простой инсталляции они совпадают, но в сложных конфигурациях с несколькими экземплярами на одной машине эти переменные различаются. CATALINA_PID задаёт файл с идентификатором процесса для корректного отслеживания.

CATALINA_OPTS - это самые интересные параметры с точки зрения производительности. Опция -Xms1024M задаёт начальный размер heap в один гигабайт, -Xmx2048M - максимальный размер в два гигабайта. Для JasperReports такие значения - разумный минимум, при больших объёмах отчётов их стоит увеличивать. Параметр -server активирует серверный режим JVM с агрессивными оптимизациями, рассчитанными на долго работающие приложения. Опция -XX:+UseParallelGC включает параллельный сборщик мусора, который хорошо подходит для серверных нагрузок с большим heap.

ExecStart и ExecStop указывают команды запуска и остановки. WantedBy=multi-user.target в секции Install отвечает за автозапуск при загрузке системы.

После сохранения файла нужно сообщить systemd о появлении нового юнита.

systemctl daemon-reload

Запуск сервиса.

systemctl start tomcat

Проверка статуса.

systemctl status tomcat

Корректный вывод показывает живой работающий сервис.

? tomcat.service - Tomcat webs servlet container
     Loaded: loaded (/etc/systemd/system/tomcat.service; disabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-09-04 06:06:39 UTC; 6s ago
    Process: 6867 ExecStart=/opt/tomcat/bin/startup.sh (code=exited, status=0/SUCCESS)
   Main PID: 6874 (java)
      Tasks: 29 (limit: 4579)
     Memory: 118.4M
        CPU: 4.427s
     CGroup: /system.slice/tomcat.service
             ??6874 /usr/lib/jvm/java-1.11.0-openjdk-amd64/bin/java -Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties -Djav>

Sep 04 06:06:39 ubuntu2204 systemd[1]: Starting Tomcat webs servlet container...
Sep 04 06:06:39 ubuntu2204 startup.sh[6867]: Tomcat started.
Sep 04 06:06:39 ubuntu2204 systemd[1]: Started Tomcat webs servlet container.

Зелёный active (running), запущенный java-процесс, двадцать девять потоков и около ста двадцати мегабайт памяти - картина здорового свежезапущенного Tomcat. Во время реальной работы цифры заметно вырастут.

Скачивание дистрибутива JasperReports и прогон автоматического инсталлятора с настройкой подключения к базе данных

Когда фундамент в виде Tomcat и MariaDB готов, можно браться за сам JasperReports. Сначала переключение в контекст пользователя tomcat и скачивание дистрибутива с SourceForge.

su - tomcat
wget https://sourceforge.net/projects/jasperserver/files/JasperServer/JasperReports%20Server%20Community%20edition%208.0.0/TIB_js-jrs-cp_8.0.0_bin.zip

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

Распаковка архива.

unzip TIB_js-jrs-cp_8.0.0_bin.zip

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

cp jasperreports-server-cp-8.0.0-bin/buildomatic/sample_conf/mysql_master.properties jasperreports-server-cp-8.0.0-bin/buildomatic/default_master.properties

Команда копирует образец настроек для MySQL/MariaDB в файл default_master.properties, который и читается установщиком. Шаблонов под разные СУБД в каталоге sample_conf лежит несколько - postgres_master, oracle_master, sqlserver_master и так далее. Каждый из них содержит настройки специфичные для своего движка.

Редактирование скопированного файла под нужды конкретной инсталляции.

nano jasperreports-server-cp-8.0.0-bin/buildomatic/default_master.properties

Из множества параметров файла критически важны несколько строк.

CATALINA_HOME = /opt/tomcat
CATALINA_BASE = /opt/tomcat

dbHost=localhost
dbUsername=master
dbPassword=password
encrypt = true

CATALINA_HOME и CATALINA_BASE указывают установщику, куда деплоить готовое веб-приложение. dbHost - адрес сервера базы данных, dbUsername и dbPassword - учётные данные пользователя, созданного ранее в MariaDB. Параметр encrypt включает шифрование чувствительных значений в конфигах JasperReports - после установки пароли в файлах не будут лежать в открытом виде.

Запуск собственно установки.

cd jasperreports-server-cp-8.0.0-bin/buildomatic/
./js-install-ce.sh

Скрипт начинает многоступенчатый процесс - проверяет окружение, создаёт схему базы данных, заполняет начальные данные, копирует WAR-файл в Tomcat, настраивает права доступа. Процесс занимает несколько минут и сопровождается обширным логом происходящего.

Удачное завершение выглядит так.

     [echo] Found Groovy in import lib directory

deploy-webapp-datasource-configs:
     [echo]  --- (app-server.xml:deploy-webapp-datasource-configs) --- 
     [echo]  jsEdition     = ce
     [echo]  warFileDistSourceDir = /opt/tomcat/jasperreports-server-cp-8.0.0-bin/buildomatic/../jasperserver.war
     [echo]  warTargetDir  = /opt/tomcat/webapps/jasperserver
     [echo]  webAppName    = jasperserver
     [echo]  webAppNameCE  = jasperserver
     [echo]  webAppNamePro = jasperserver-pro
     [echo]  webAppNameSrc = jasperserver 
     [echo]  webAppNameDel = jasperserver, warTargetDirDel = /opt/tomcat/webapps/jasperserver
     [copy] Copying 9 files to /opt/tomcat/webapps/jasperserver

scalableAdhoc-refinement:

deploy-webapp-ce:

install-normal-ce:
     [echo] Installation successfully completed!

BUILD SUCCESSFUL
Total time: 1 minute 26 seconds
Checking Ant return code: OK
----------------------------------------------------------------------

Сообщение Installation successfully completed! и BUILD SUCCESSFUL подтверждают, что приложение развёрнуто. Минута двадцать шесть секунд - типичное время на скромном сервере, на более мощных машинах процесс идёт быстрее.

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

nano /opt/tomcat/conf/catalina.policy

В файл добавляется блок разрешений.

grant codeBase "file:/groovy/script" {
    permission java.io.FilePermission "${catalina.home}${file.separator}webapps${file.separator}
    jasperserver-pro${file.separator}WEB-INF${file.separator}classes${file.separator}-", "read";
    permission java.io.FilePermission "${catalina.home}${file.separator}webapps${file.separator}
    jasperserver-pro${file.separator}WEB-INF${file.separator}lib${file.separator}*", "read";
    permission java.util.PropertyPermission "groovy.use.classvalue", "read";
};

Этот блок разрешает Groovy-скриптам читать файлы из определённых каталогов веб-приложения. Без этих разрешений Java Security Manager блокировал бы попытки динамического кода обращаться к ресурсам приложения, и часть функциональности JasperReports просто не работала бы.

Аналогичная настройка делается в applicationContext.xml.

nano /opt/tomcat/webapps/jasperserver/WEB-INF/applicationContext.xml

В файл добавляется конфигурация бина с разрешениями для protection domain.

<bean id="reportsProtectionDomainProvider" class="com.jaspersoft.jasperserver.api.
engine.jasperreports.util.PermissionsListProtectionDomainProvider">
<property name="permissions">
<list>
    <bean class="java.io.FilePermission">
        <constructor-arg value="${catalina.home}${file.separator}webapps
        ${file.separator}jasperserver-pro${file.separator}
        WEB-INF${file.separator}classes${file.separator}-"/>
        <constructor-arg value="read"/>
    </bean>
    <bean class="java.io.FilePermission">
        <constructor-arg value="${catalina.home}${file.separator}webapps
        ${file.separator}jasperserver-pro${file.separator}WEB-INF
        ${file.separator}lib${file.separator}*"/>
        <constructor-arg value="read"/>
    </bean>
</list>
</property>
</bean>

Spring Framework, на котором построен JasperReports, использует такие декларативные конфигурации бинов для настройки внутренних компонентов. Этот бин управляет тем, какие разрешения получают отчёты при выполнении - дублирует то, что описано в catalina.policy, но на уровне самого приложения.

Завершение работы под пользователем tomcat и перезапуск сервиса для применения всех изменений.

exit
systemctl restart tomcat

После перезапуска Tomcat подхватывает развёрнутое приложение и публикует его на восьмитысячном порту.

Первый вход в веб-интерфейс JasperReports и настройка обратного прокси Nginx для удобного доступа по доменному имени

После успешного запуска Tomcat по адресу http://YOUR_SERVER_IP_ADDRESS:8080/jasperserver/ открывается страница входа JasperReports. Адрес длинный и неудобный - сначала IP, потом порт, потом контекстный путь приложения. Это решается проксированием через Nginx чуть позже, но пока для проверки работоспособности можно зайти и так.

Дефолтные учётные данные администратора - jasperadmin/jasperadmin. Они хорошо известны и должны быть сменены сразу после первого входа на боевом сервере. Никаких других учёток в свежей инсталляции нет, и оставлять стандартный пароль для веб-интерфейса с доступом ко всей бизнес-отчётности - прямой путь к компрометации системы.

После входа открывается основной дашборд с навигацией по репозиторию. Тут можно создавать новые отчёты, загружать готовые шаблоны JRXML, настраивать источники данных, управлять пользователями и ролями, планировать автоматическую генерацию отчётов по расписанию. Интерфейс из 2010-х годов на вид, но функциональность за фасадом серьёзная.

Прямой доступ через IP и порт неудобен, и тут пора подключать Nginx как реверс-прокси.

apt install nginx

После установки создаётся отдельный конфиг под наш виртуальный хост.

nano /etc/nginx/conf.d/jasperreports.conf

Содержимое получается компактным.

upstream tomcat {
    server 127.0.0.1:8080 weight=100 max_fails=5 fail_timeout=5;
    }

server {
    server_name jasper.example.com;

location = / {
    return 301 http://jasper.example.com/jasperserver/;
    }

location / {
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://tomcat/;
    }
}

Блок upstream объявляет именованный пул бэкендов с одним сервером Tomcat на локальном порту 8080. Параметр weight=100 фактически бессмыслен для одного бэкенда - он играет роль только при балансировке между несколькими серверами. Параметры max_fails=5 и fail_timeout=5 настраивают пассивную проверку здоровья - после пяти неудачных запросов в течение пяти секунд сервер помечается как недоступный.

Server-блок ловит запросы на jasper.example.com. Первая локация с точным совпадением = отлавливает запросы к корню сайта и редиректит их на /jasperserver/. Это удобство для пользователя - не нужно набирать длинный путь руками, достаточно зайти на основной домен. Вторая локация со слешем покрывает все остальные пути и проксирует их на пул tomcat. Заголовки X-Forwarded-Host, X-Forwarded-Server и X-Forwarded-For передают приложению информацию об оригинальном клиенте - без них Tomcat видел бы все запросы как идущие с 127.0.0.1.

Перед перезапуском Nginx обязательна проверка синтаксиса.

nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Если конфиг битый, Nginx подскажет на какой строке проблема. На корректном конфиге следует рестарт сервиса.

systemctl restart nginx

Проверка статуса не помешает.

systemctl status nginx
? nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2022-09-04 14:51:10 UTC; 7s ago
       Docs: man:nginx(8)
    Process: 7644 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 7645 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 7646 (nginx)
      Tasks: 3 (limit: 4579)
     Memory: 3.3M
        CPU: 45ms
     CGroup: /system.slice/nginx.service
             ??7646 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ??7647 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
             ??7648 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""

Sep 04 14:51:10 ubuntu2204 systemd[1]: Starting A high performance web server and a reverse proxy server...
Sep 04 14:51:10 ubuntu2210 systemd[1]: Started A high performance web server and a reverse proxy server.

Прокси работает, потребляет смешные три мегабайта памяти и готов принимать запросы. Теперь по адресу http://jasper.example.com открывается тот же JasperReports, но с короткого и понятного URL.

Практическая ценность развёрнутой системы и сценарии её применения в типовых корпоративных задачах

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

Главная сила JasperReports проявляется в комплексных сценариях интеграции. Платформа умеет подключаться к десяткам типов источников данных - реляционные базы вроде PostgreSQL и Oracle, NoSQL-хранилища, JSON и XML по HTTP, CSV-файлы, Hadoop, Spark, MongoDB. Один отчёт может объединять данные из нескольких источников, что особенно ценно в сложной корпоративной среде с разрозненными системами.

Шаблоны отчётов пишутся в специальном формате JRXML, который похож на HTML, но заточен под печатные документы со сложной разметкой. Существует визуальный редактор Jaspersoft Studio для создания шаблонов мышкой, но опытные разработчики часто пишут JRXML руками для тонкого контроля над результатом. После создания шаблон загружается в репозиторий JasperReports и становится доступным для всех пользователей системы.

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

Освоение JasperReports открывает дорогу в нишу корпоративной отчётности, которая остаётся востребованной несмотря на моду на современные BI-инструменты вроде Tableau и Power BI. JasperReports выигрывает в сценариях, где нужны именно печатные документы строгого формата - официальные отчёты для регуляторов, накладные, акты, договоры с переменными частями. Современные дашборды красивы на экране, но плохо подходят для печати на бумаге, и тут JasperReports со своей пиксель-перфектной вёрсткой остаётся вне конкуренции. Для разработчиков этот инструмент даёт возможность встраивать профессиональную отчётность в свои приложения без необходимости писать собственный движок генерации PDF и Excel - задача, которая в прошлом отнимала месяцы работы целых команд.