title: Apache POI OPCPackage unable to save Jasper Report generated XLSX
body: <p>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: <a href="http://www.quicklyjava.com/create-password-protected-excel-using-apache-poi/" rel="nofollow">http://www.quicklyjava.com/create-password-protected-excel-using-apache-poi/</a>.</p>
  
  <p>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.</p>
  
  <p>After debugging I believe I was able to pinpoint the issue. </p>
  
  <p>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 <b>'application/vnd.openxmlformats-package.core-properties+xml'</b>. However, this part is not found in a Jasper Report generated XLSX file thus remaining null. </p>
  
  <pre><code>// Check OPC compliance rule M4.1
  if (part.getContentType().equals(ContentTypes.CORE_PROPERTIES_PART)) {
      if (!hasCorePropertiesPart) {hasCorePropertiesPart = true;
  </code></pre>
  
  <p>If this passes, a few lines below we assign this.packageProperties:</p>
  
  <pre><code>// Core properties case-- use first CoreProperties part we come across
  // and ignore any subsequent ones
  if (unmarshallPart instanceof PackagePropertiesPart &amp;&amp; needCorePropertiesPart) {
      this.packageProperties = (PackagePropertiesPart) unmarshallPart;
  </code></pre>
  
  <p>The file opens fine, however when I try to save the file the <strong><em>saveImpl</em></strong> method calls a method called <strong><em>addPackagePart</em></strong> passing in the 'packageProperties' variable. The class then throws an exception because the field is null. From <strong><em>ZipPackage#saveImpl</em></strong></p>
  
  <pre><code>// 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 &amp;&amp; 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);
  </code></pre>
  
  <p>Here is the exception:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>The Jasper Reports code is the standard code to export a XLSX file.</p>
  
  <pre><code>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();
  </code></pre>
  
  <p>This creates the XLSX just fine, however when I extract the contents and look through the package I don't see the properties Part <b>application/vnd.openxmlformats-package.core-properties+xml</b> which apparently is required by Apache POI to save the file.</p>
  
  <p>The encryption code:</p>
  
  <pre><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);
  </code></pre>
  
  <p>Dependency versions:</p>
  
  <p>jasper reports - 4.7.1 (I also tried 6.0.2 with the same result)</p>
  
  <p>apache poi - 3.11</p>
  
  <p>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?</p>
  
  <p>I'm hoping someone has seen something similar that they can help me.</p>
  
  <p>Cheers and thank you!</p>