Jasperreports не работает на сервере Linux без X11: «Ошибка инициализации графической среды».

#java #linux #docker #jasper-reports #headless

#java #linux #docker #jasper-отчеты #без головы

Вопрос:

У меня огромная проблема. Я кодировал приложение, которое использует Jasperreports. Все было прекрасно, когда я работал на месте. Но затем, когда я настроил свое приложение, когда я запускаю метод, который должен создать файл PDF, я получаю сообщение об ошибке:

 Request processing failed; nested exception is net.sf.jasperreports.engine.JRRuntimeException: Error initializing graphic environment.
  

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

 -DJava.awt.headless=true
  

проблема решилась бы сама собой. Но это не так.

Вот как это выглядит в Dockerfile :

 ENTRYPOINT [ "java", "-Djava.awt.headless=true", "-jar", "/var/generator/generator-0.0.1-SNAPSHOT.jar" ]
  

Чтобы быть уверенным, что я ничего не забыл, вот код метода, который генерирует файлы PDF, возможно, там что-то не так…

Ошибка исходит из этой строки:

 mainReport = JasperFillManager.fillReport(report, invoiceMap, new JREmptyDataSource());
  

И вот способ:

 public byte[] generateInvoicePDF(Invoice invoice) {

    /* Map to fill in main JasperReport */
    Map<String, Object> invoiceMap = new HashMap<String, Object>();
    invoiceMap.put("FIRST_NAME", invoice.getFirstName());
    invoiceMap.put("LAST_NAME", invoice.getLastName());
    invoiceMap.put("REGON", invoice.getRegon());
    invoiceMap.put("NIP", invoice.getNip());
    invoiceMap.put("BUSINESS_NAME", invoice.getBusinessName());
    invoiceMap.put("BUSINESS_LOCATION", invoice.getBusinessLocation());
    invoiceMap.put("ORDER_DATE", invoice.getOrderDate());
    invoiceMap.put("RECEPTION_DATE", invoice.getReceptionDate());
    invoiceMap.put("CONTRACT_DATE", invoice.getContractDate());
    invoiceMap.put("CONTRACTOR_COMMENT", invoice.getContractorComment());
    invoiceMap.put("INVOICE_NUMBER", invoice.getInvoiceNumber());
    invoiceMap.put("INVOICE_ISSUE_DATE", invoice.getInvoiceIssueDate());

    JasperPrint mainReport;
    JasperPrint tableReport;
    try {
        InputStream invoiceJasper = InvoiceGeneratorServiceImpl.class.getResourceAsStream("/invoices/invoice.jrxml");
        InputStream taskTableJasper = InvoiceGeneratorServiceImpl.class.getResourceAsStream("/invoices/invoice_sub.jrxml");
        JasperDesign mainReportDesign = JRXmlLoader.load(invoiceJasper);
        JasperDesign tableReportDesign = JRXmlLoader.load(taskTableJasper);
        JasperReport report = JasperCompileManager.compileReport(mainReportDesign);
        JasperReport table = JasperCompileManager.compileReport(tableReportDesign);

        /* List to hold Items */
        List<InvoiceTask> taskList = new ArrayList<>();

        /* Add Items to List */
        taskList.addAll(invoice.getInvoiceTasks());

        /* Convert List to JRBeanCollectionDataSource */
        JRBeanCollectionDataSource itemsJRBean = new JRBeanCollectionDataSource(taskList);

        /* Map to hold Jasper report Parameters */
        Map<String, Object> taskListMap = new HashMap<String, Object>();
        taskListMap.put("TasksDataSource", itemsJRBean);
        taskListMap.put("ADDITIONAL_COSTS", invoice.getAdditionalCosts());

        mainReport = JasperFillManager.fillReport(report, invoiceMap, new JREmptyDataSource());
        tableReport = JasperFillManager.fillReport(table, taskListMap, new JREmptyDataSource());
        List<JasperPrint> printList = new ArrayList<>();
        printList.add(mainReport);
        printList.add(tableReport);

        JRPdfExporter exporter = new JRPdfExporter();
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        exporter.setExporterInput(SimpleExporterInput.getInstance(printList)); //Set as export input my list with JasperPrint s
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(byteArrayOutputStream));
        SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
        configuration.setCreatingBatchModeBookmarks(true); //add this so your bookmarks work, you may set other parameters
        exporter.setConfiguration(configuration);
        exporter.exportReport();

        return byteArrayOutputStream.toByteArray();
    } catch (JRException e) {
        e.printStackTrace();
    }

    return new byte[0];
}
  

И еще немного о трассировке стека:

 2019-04-24 08:43:57.338 ERROR 1 --- [io-8080-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is net.sf.jasperreports.engine.JRRuntimeException: Error initializing graphic environment.] with root cause

java.lang.NullPointerException: null
    at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264) ~[na:1.8.0_201]
    at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219) ~[na:1.8.0_201]
    at sun.awt.FontConfiguration.init(FontConfiguration.java:107) ~[na:1.8.0_201]
    at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774) ~[na:1.8.0_201]
    at sun.font.SunFontManager$2.run(SunFontManager.java:431) ~[na:1.8.0_201]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_201]
    at sun.font.SunFontManager.<init>(SunFontManager.java:376) ~[na:1.8.0_201]
    at sun.awt.FcFontManager.<init>(FcFontManager.java:35) ~[na:1.8.0_201]
    at sun.awt.X11FontManager.<init>(X11FontManager.java:57) ~[na:1.8.0_201]
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_201]
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_201]
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_201]
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_201]
    at java.lang.Class.newInstance(Class.java:442) ~[na:1.8.0_201]
    at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83) ~[na:1.8.0_201]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_201]
    at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74) ~[na:1.8.0_201]
    at sun.java2d.SunGraphicsEnvironment.getFontManagerForSGE(SunGraphicsEnvironment.java:201) ~[na:1.8.0_201]
    at sun.java2d.SunGraphicsEnvironment.getAvailableFontFamilyNames(SunGraphicsEnvironment.java:235) ~[na:1.8.0_201]
    at sun.java2d.SunGraphicsEnvironment.getAvailableFontFamilyNames(SunGraphicsEnvironment.java:263) ~[na:1.8.0_201]
    at sun.java2d.HeadlessGraphicsEnvironment.getAvailableFontFamilyNames(HeadlessGraphicsEnvironment.java:94) ~[na:1.8.0_201]
    at net.sf.jasperreports.engine.util.JRGraphEnvInitializer.initializeGraphEnv(JRGraphEnvInitializer.java:58) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at net.sf.jasperreports.engine.fill.BaseReportFiller.<init>(BaseReportFiller.java:136) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at net.sf.jasperreports.engine.fill.JRBaseFiller.<init>(JRBaseFiller.java:273) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at net.sf.jasperreports.engine.fill.JRVerticalFiller.<init>(JRVerticalFiller.java:79) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at net.sf.jasperreports.engine.fill.JRFiller.createBandReportFiller(JRFiller.java:251) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at net.sf.jasperreports.engine.fill.JRFiller.createReportFiller(JRFiller.java:272) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at net.sf.jasperreports.engine.fill.JRFiller.fill(JRFiller.java:156) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at net.sf.jasperreports.engine.fill.JRFiller.fill(JRFiller.java:145) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.java:758) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at net.sf.jasperreports.engine.JasperFillManager.fillReport(JasperFillManager.java:1074) ~[jasperreports-6.7.1.jar!/:6.7.1]
    at com.generator.service.impl.InvoiceGeneratorServiceImpl.generateInvoicePDF(InvoiceGeneratorServiceImpl.java:68) ~[classes!/:na]
    at com.generator.rest.ContractorController.getPdf(ContractorController.java:165) ~[classes!/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
  

Комментарии:

1. Происходит ли сбой образа Docker с указанной выше ошибкой и на вашем локальном экземпляре?

2. @Marvin да, я экспортировал его с помощью Jaspersoft Studio в виде файла jar, а затем импортировал его в Gradle imports.

3. @J_D — Я нахожусь в своем локальном экземпляре. Но когда я запускаю его на своем локальном компьютере без docker, все в порядке. Все идет не так, когда приложение запускается в контейнере docker

4. @Marvin да, я использую Alpine. Я изучил тему, и добавление RUN apk add ttf-dejavu в мой файл Dockerfile решило проблему.

5. @Marvin я бы с удовольствием, но еще больше мне хотелось бы дать вам положительный отзыв и принять ответ, потому что именно ваш комментарий привел меня к правильному ответу 🙂

Ответ №1:

Это похоже на проблему с отсутствующим шрифтом.

Если вы используете Alpine, возможно, вы видите проблему # 73, которая имеет ту же самую соответствующую трассировку стека. Согласно этой проблеме, установка ttf-dejavu пакета должна решить вашу проблему.

Добавьте это в свой файл dockerfile:

 RUN apk add ttf-dejavu
  

Ответ №2:

Я смог исправить это, установив пакеты fontconfig и urw-fonts.

 yum install fontconfig
yum install urw-fonts
  

смотрите проблему с github