Hot questions for Using JasperReports in apache poi

Question:

I am trying to generate Jasper Excel report.I am getting the exception like

java.lang.NoSuchMethodError: org.apache.poi.hssf.usermodel.HSSFSheet.setColumnWidth(II)V
at net.sf.jasperreports.engine.export.JRXlsExporter.setColumnWidth(JRXlsExporter.java:212)
at net.sf.jasperreports.engine.export.JRXlsAbstractExporter.setColumnWidths(JRXlsAbstractExporter.java:654)
at net.sf.jasperreports.engine.export.JRXlsAbstractExporter.exportPage(JRXlsAbstractExporter.java:527)
at net.sf.jasperreports.engine.export.JRXlsAbstractExporter.exportReportToStream(JRXlsAbstractExporter.java:423)
at net.sf.jasperreports.engine.export.JRXlsAbstractExporter.exportReport(JRXlsAbstractExporter.java:207)
at com.pagesolutions.controller.ReportingController.doGet(ReportingController.java:177)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1074)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

My Java code

if(formatType.matches("XLS")){
            String jrxml = "/home/madhu/report1.jrxml";
            list = (List<Reports>) session.getAttribute("customersList");
            //InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(jrxml);
            try {
                //jasperReport = JasperCompileManager.compileReport(jrxml);
                //jasperDesign = JRXmlLoader.load(jrxml);
                jasperReport = JasperCompileManager.compileReport(jrxml);
                JRDataSource datasource = new JRBeanCollectionDataSource(list, true);
                jasperPrint = JasperFillManager.fillReport(jasperReport, null, datasource);
                String output="/home/madhu/reports/report.xls";
                //JasperExportManager.exportReportToPdfFile(jasperPrint, output);
                JRXlsExporter exporterXLS = new JRXlsExporter();
                exporterXLS.setParameter(JRXlsExporterParameter.JASPER_PRINT, jasperPrint);
                exporterXLS.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.TRUE);
                exporterXLS.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);
                exporterXLS.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
                exporterXLS.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);
                exporterXLS.setParameter(JRExporterParameter.OUTPUT_FILE_NAME, output);
                exporterXLS.exportReport();
                 ServletOutputStream servletOutputStream = response.getOutputStream();
                 String fileName = "report.xls";
                 FileInputStream fileToDownload = new FileInputStream(output);
                 response.setContentType("application/vnd.ms-excel");
                 response.setHeader("Content-Disposition", "attachment; filename="+ fileName);
                 ByteArrayOutputStream output1 = new ByteArrayOutputStream(); 
                 int readBytes = 0;
                 byte[] buffer = new byte[10000];
                 while ((readBytes = fileToDownload.read(buffer, 0, 10000)) != -1) {
                     servletOutputStream.write(buffer, 0, readBytes);
                 }

                 output1.flush();
                 output1.close();
                 fileToDownload.close();
            } catch (JRException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

I already added poi-3.2-FINAL.jar file in my lib but still getting the above exception

Can any one help me please....


Answer:

If the project builds but you get this error at runtime, it can mean only one thing: you're building and running with different library versions. The version you built against has this method while the one you ran with doesn't. It could have also happened that you have multiple versions on the classpath and the wrong one is loaded first. I don't know how you build your project, but if you're using some kind of dependency manager, analyse the structure for duplicate POI versions. If you're not using any dependency manager... well... make sure manually that libraries match at build- and runtime, but you really should switch to a build tool that does this for you so that Jasper can automatically drag in the correct version it depends on.

Important note: Since Jasper already depends on POI, there's no reason for your project to declare that dependency again. It's only asking for trouble of the variety you're seeing now. You just declare your dependency to Jasper and let your dependency manager drag in the correct version of POI. Somehow, fundamental misunderstanding of dependency management is common even among senior developers.

If, on the other hand, another library you depend on also drags in a different version of POI, you need to either find compatible versions of both third-party libraries (so that they need the same POI), or explicitly‚Äč exclude POI from one of them, cross your fingers and hope for the best.

Question:

I have a Java application that uses Jasper to create docx. Jasper uses poi to export to docx. There are a few issues with the documents generated with poi and I was wondering if there are any good alternatives to poi. Apart from modifying the source code of Jasper, is there a way to link the new jar to Jasper so that it uses it instead of poi? If i write my own docx exporter can it be easily linked so that Jasper will use it? Does anybody know an article that explains how to link a custom jar (or an alternative jar) to Jasper?


Answer:

In this post, it appears the author is converting Jasper's XHTML output into docx format using docx4j. Perhaps that results in a 'better' docx, without the effort of -rewriting Jasper's docx exporter?

Question:

I have an application which kicks out a various documents in various formats. This is it's main purpose. I am using japser for most of the non excel documents and now I realise I may have made a mistake! (I can't afford Aspose !). I am operating in a Windows environment.

Essentially many of the documents are produced in both odt and doc format. The requirement for the Word version is for doc so as to be compatible for the maximum number of users (who are outside my organisation).

For this I am exporting in rtf format to a doc suffixed file. So far so good except the tables are exported as text input boxes not tables and the users are very upset that they can't tab between cells (we missed this in testing). This seems like something that cannot be sorted out simply in report configuration. the rtf export simply does not export tables as tables.

I considered saving as docx and using poi to convert to doc but this doesn't seem so simple

  • lack of an interface between xwpf and hwpf.
  • there seems to be a bug in jasper export to docx which sets the margins to 0 and causes printing warnings. Probably surmountable but as that was a quick & dirty fix idea it is beginning to seem more effort than such a messy solution is worth.

This leaves me with the idea of converting the odt files to doc. I've had a quick look at jodconverter but it seems so long since anyone has done anything with this that I'm not convinced it is the way to go (I'm also not sure it is compatible with OpenOffice 4.1 which is what I've downloaded although I guess I could back out of that?).

So the main part of this question is - is there a reasonably simple way to convert odt to doc/rtf in java code (I don't really want the overhead of sending documents to external conversion sites). I guess an aside would be for any serious Jasper guru's - is there a way of exporting to rtf/doc format with tables as table objects rather than collections of adjacent text input boxes ?

I've tried to lay this out in full, apologies if it's a little verbose. Any help greatly appreciated (the simpler the solution the better!).


Answer:

You can try JODConverter which does conversion between ODT and DOC(X) but there's litte documentation about this library and it's no longer maintained.

According to the little doc there is (here and here), here's a little example to convert an odt to a doc file (you need to have OpenOffice or LibreOffice installed locally)

OfficeManager officeManager = new DefaultOfficeManagerConfiguration().setOfficeHome(new File("<path to office home>")).buildOfficeManager();
officeManager.start();

OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
converter.convert(new File("test.odt"), new File("test.doc"));

officeManager.stop();

<path to office home> must be replaced with the path to the home directory of LibreOffice or OpenOffice (the directory that contains the soffice.bin file).

Question:

The overall plan of action is to generate an XLSX file using JasperReports API, then feeding it into Apache POI's encryption code. Example found here: http://www.quicklyjava.com/create-password-protected-excel-using-apache-poi/.

Unfortunately I am unable to encrypt the file when it is generated from Jasper Report, however if I were to create a file inside MS Excel and load it through the code then it encrypts just fine. So the Apache POI library works fine with files coming from Excel.

After debugging I believe I was able to pinpoint the issue.

We use the OPCPackage class to open the file (or in another case load the Input Stream), the class has a method getParts() which sets all the fields. One in particular is packageProperties. It seems that packageProperties is loading the specific Package Part 'application/vnd.openxmlformats-package.core-properties+xml'. However, this part is not found in a Jasper Report generated XLSX file thus remaining null.

// Check OPC compliance rule M4.1
if (part.getContentType().equals(ContentTypes.CORE_PROPERTIES_PART)) {
    if (!hasCorePropertiesPart) {hasCorePropertiesPart = true;

If this passes, a few lines below we assign this.packageProperties:

// Core properties case-- use first CoreProperties part we come across
// and ignore any subsequent ones
if (unmarshallPart instanceof PackagePropertiesPart && needCorePropertiesPart) {
    this.packageProperties = (PackagePropertiesPart) unmarshallPart;

The file opens fine, however when I try to save the file the saveImpl method calls a method called addPackagePart passing in the 'packageProperties' variable. The class then throws an exception because the field is null. From ZipPackage#saveImpl

// If the core properties part does not exist in the part list,
// we save it as well
if (this.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES).size() == 0 && this.getPartsByRelationshipType(PackageRelationshipTypes.CORE_PROPERTIES_ECMA376).size() == 0    ) {
    logger.log(POILogger.DEBUG,"Save core properties part");
        // Add core properties to part list ...
        addPackagePart(this.packageProperties);

Here is the exception:

org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException: Fail to save: an error occurs while saving the package : part
    at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:503)
    at org.apache.poi.openxml4j.opc.OPCPackage.save(OPCPackage.java:1425)
    at com.krfs.web.controller.report.BaseReportController.encryptXlsxOutputStream(BaseReportController.java:717)
    at com.krfs.web.controller.report.BaseReportController.generateJasperReportOutput(BaseReportController.java:547)
    at com.krfs.web.controller.report.BaseReportController.processReportRunningAndGeneration(BaseReportController.java:382)
    at com.krfs.web.controller.report.StandardReportController.processReportRunningAndGeneration(StandardReportController.java:25)
    at com.krfs.web.controller.report.StandardReportController.run(StandardReportController.java:82)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:177)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.krfs.web.filter.RedirectFilter.doFilter(RedirectFilter.java:62)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.krfs.web.filter.XssFilter.doFilter(XssFilter.java:17)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.authentication.switchuser.SwitchUserFilter.doFilter(SwitchUserFilter.java:181)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at com.krfs.web.filter.CustomLoginFilter.doFilter(CustomLoginFilter.java:156)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at com.krfs.web.filter.CsrfFilter.doFilter(CsrfFilter.java:52)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at fr.xebia.servlet.filter.ExpiresFilter.doFilter(ExpiresFilter.java:1243)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.krfs.web.filter.SecurityHeadersFilter.doFilter(SecurityHeadersFilter.java:22)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.krfs.web.filter.LoggingFilter.doFilter(LoggingFilter.java:44)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:108)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:744)
Caused by: java.lang.IllegalArgumentException: part
    at org.apache.poi.openxml4j.opc.OPCPackage.addPackagePart(OPCPackage.java:873)
    at org.apache.poi.openxml4j.opc.ZipPackage.saveImpl(ZipPackage.java:448)
    ... 104 more

The Jasper Reports code is the standard code to export a XLSX file.

JRXlsAbstractExporter exporter = new JRXlsxExporter();

JasperPrint jasperPrint = fillJasperPrint(reportMessenger, reportFile, reportParameters);

exporter.setParameter(JRXlsExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, outputStream);
exporter.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);                    
exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);

exporter.exportReport();

This creates the XLSX just fine, however when I extract the contents and look through the package I don't see the properties Part application/vnd.openxmlformats-package.core-properties+xml which apparently is required by Apache POI to save the file.

The encryption code:

POIFSFileSystem fs = new POIFSFileSystem();
        EncryptionInfo info = new EncryptionInfo(fs, EncryptionMode.agile);
        Encryptor enc = info.getEncryptor();
        enc.confirmPassword(reportPassword);

        try (OPCPackage opc = OPCPackage.open(new ByteArrayInputStream(xlsxOutputStream.toByteArray()))) {
            OutputStream os = enc.getDataStream(fs);
            opc.save(os);
        }

        fs.writeFilesystem(encryptedOutputStream);

Dependency versions:

jasper reports - 4.7.1 (I also tried 6.0.2 with the same result)

apache poi - 3.11

So my question is this, how can I get these two libraries to work in harmony so that I can encrypt the file coming out from jasper reports? Is there a way to inject the output type part core-properties+xml before exporting the file? Is there a way around the Apache POI library to skip finding that part?

I'm hoping someone has seen something similar that they can help me.

Cheers and thank you!


Answer:

As far as I can tell, Jasper Reports is writing slightly non-spec-compliant files. Not so far out of support that Excel can't work with them, but non-compliant all the same.

Apache POI can read these files just fine, as shown in unit tests like this one. Apache POI can save them too, if opened and saved via something like XSSFWorkbook or similar, which triggers the creation of the missing part.

Unfortunately for your use case, you were working down at the OPCPacakge level, and there was a POI bug there. This has been fixed in r1662971, and will be in POI 3.12 beta 2 and later.

For now, you can trigger the core properties creation by changing these lines:

OPCPackage opc = OPCPackage.open(inputFilePath, PackageAccess.READ_WRITE);
OutputStream os = enc.getDataStream(fs);
opc.save(os);

To instead be:

OPCPackage opc = OPCPackage.open(inputFilePath, PackageAccess.READ_WRITE);
OutputStream os = enc.getDataStream(fs);
opc.getProperties();
opc.save(os);

Once you're on POI 3.12 beta 2 or later, you won't need that extra call to getProperties().

Also, the code in the github you linked to has some deprecated calls in it, you might want to look at TestEncryptor.encryptPackageWithoutCoreProperties() to see how to slightly tweak it to work without deprecated methods.