diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 27b87c4..9511fee 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: distribution: [ 'temurin' ] - java: [ '17', '21' ] + java: [ '17', '21', '25'] steps: - uses: actions/checkout@v3 - name: Setup java diff --git a/pom.xml b/pom.xml index 42dcccf..d89afad 100644 --- a/pom.xml +++ b/pom.xml @@ -13,8 +13,8 @@ UTF-8 - 11 - 11 + 17 + 17 6.0.0 1.16 3.0.5 diff --git a/src/main/java/de/pdv/apex/PdfServlet.java b/src/main/java/de/pdv/apex/PdfServlet.java index 89abc67..eeb5919 100644 --- a/src/main/java/de/pdv/apex/PdfServlet.java +++ b/src/main/java/de/pdv/apex/PdfServlet.java @@ -1,5 +1,6 @@ package de.pdv.apex; +import jakarta.servlet.ServletException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; @@ -27,14 +28,19 @@ @WebServlet(name = "pdf", urlPatterns = {"/pdf"}) public class PdfServlet extends HttpServlet { private static final Logger logger = Logger.getLogger(PdfServlet.class.getName()); - + private static FopFactory fopFactory; private static final TransformerFactory tFactory = TransformerFactory.newInstance(); /** * Init the servlet and setup TransformerFactory */ @Override - public void init() { + public void init() throws ServletException { + // Java 25 may need old parser + System.setProperty("javax.xml.parsers.SAXParserFactory", + "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"); + System.setProperty("org.xml.sax.driver", + "com.sun.org.apache.xerces.internal.parsers.SAXParser"); try { tFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); tFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); @@ -43,6 +49,26 @@ public void init() { } catch (TransformerConfigurationException e) { logger.warning(String.format("TransformerConfigurationException while setup TransformerFactory - possible security issue: %s", e.getMessage())); } + try { + synchronized (PdfServlet.class) { + if (fopFactory == null) { + fopFactory = FopFactory.newInstance(new File(".").toURI()); + fopPreload(); + } + } + } catch (Exception e) { + throw new ServletException("FOP init failed", e); + } + } + + // 🔑 Preload event-model.xml: Dummy Fop anlegen + private static void fopPreload() { + try (java.io.ByteArrayOutputStream dummyOut = new java.io.ByteArrayOutputStream()) { + FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); + fopFactory.newFop(MIME_PDF, foUserAgent, dummyOut); + } catch (Exception e) { + logger.warning("FOP preload failed (can be ignored if once-only): " + e.getMessage()); + } } /** @@ -60,7 +86,6 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { logger.log(Level.INFO,"doGet"); try { response.setContentType("application/pdf"); - FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); Fop fop = fopFactory.newFop(MIME_PDF, response.getOutputStream()); Transformer transformer = tFactory.newTransformer(); InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("samples/helloWorld.fo"); @@ -69,7 +94,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) { transformer.transform(src, res); logger.log(Level.INFO,"Process complete"); } catch (Exception ex) { - logger.log(Level.SEVERE,String.format("Error while processing fop transformation %s", ex.getMessage())); + logger.log(Level.SEVERE,String.format("Error while processing fop transformation %s", ex)); } } @@ -108,8 +133,6 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) response.setContentType("application/pdf"); response.setHeader("Content-disposition", "attachment; filename=" + pdfFileName); - FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); - FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); // Construct fop with desired output format Fop fop = fopFactory.newFop(MIME_PDF, foUserAgent, response.getOutputStream()); @@ -128,8 +151,10 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) logger.log(Level.INFO,"Process complete"); } catch (Exception ex) { - context.log(ex.getMessage(), ex); - logger.log(Level.SEVERE,String.format("Error while processing fop transformation %s", ex.getMessage())); + if (context != null) { + context.log(ex.getMessage(), ex); + } + logger.log(Level.SEVERE,String.format("Error while processing fop transformation %s", ex)); } } diff --git a/src/test/java/de/pdv/apex/PdfServletTest.java b/src/test/java/de/pdv/apex/PdfServletTest.java index c537507..d96dfa6 100644 --- a/src/test/java/de/pdv/apex/PdfServletTest.java +++ b/src/test/java/de/pdv/apex/PdfServletTest.java @@ -1,5 +1,6 @@ package de.pdv.apex; +import jakarta.servlet.ServletException; import org.apache.commons.io.IOUtils; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeEach; @@ -41,6 +42,7 @@ void doGet() throws Exception { when(servlet.getServletConfig()).thenReturn(servletConfig); when(response.getOutputStream()).thenReturn(outputStream); when(response.getWriter()).thenReturn(new PrintWriter(new StringWriter())); + servlet.init(); servlet.doGet(request, response); System.out.println("Check for header %PDF-1.4%..."); byte[] b = {0x25,0x50,0x44,0x46,0x2D,0x31,0x2E,0x34,0x0A}; // @@ -62,6 +64,7 @@ void doPost() throws Exception { when(request.getParameter("template")).thenReturn(xsltFileContent); when(request.getParameter("xml")).thenReturn(xmlFileContent); when(response.getWriter()).thenReturn(new PrintWriter(new StringWriter())); + servlet.init(); servlet.doPost(request, response); System.out.println("Check for header %PDF-1.4%..."); byte[] b = {0x25,0x50,0x44,0x46,0x2D,0x31,0x2E,0x34,0x0A}; // @@ -70,7 +73,7 @@ void doPost() throws Exception { } @Test - void init() { + void init() throws ServletException { when(servlet.getServletConfig()).thenReturn(servletConfig); assertNotNull(servlet); servlet.init();