Introduce FOP servlet.
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 17 May 2022 11:55:14 +0000 (13:55 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 17 May 2022 11:55:14 +0000 (13:55 +0200)
org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java [new file with mode: 0644]

diff --git a/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java
new file mode 100644 (file)
index 0000000..bba0d30
--- /dev/null
@@ -0,0 +1,169 @@
+package org.argeo.app.servlet.publish;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.FopFactoryBuilder;
+import org.apache.xalan.processor.TransformerFactoryImpl;
+import org.apache.xmlgraphics.io.Resource;
+import org.apache.xmlgraphics.io.ResourceResolver;
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.ContentRepository;
+import org.argeo.api.acr.ContentSession;
+import org.argeo.cms.auth.RemoteAuthUtils;
+import org.argeo.cms.servlet.ServletHttpRequest;
+import org.argeo.util.LangUtils;
+
+/**
+ * A servlet transforming an XML view of the data to either FOP or PDF.
+ */
+public class FopServlet extends HttpServlet {
+       private static final long serialVersionUID = 6906020513498289335L;
+
+       private ContentRepository contentRepository;
+
+       private DocumentBuilderFactory documentBuilderFactory;
+       private TransformerFactory transformerFactory;
+       private Templates foTemplates;
+
+       private String xslUrl;
+
+       @Override
+       protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+               String servletPath = req.getServletPath();
+               String ext = servletPath.substring(servletPath.lastIndexOf('.'));
+               servletPath = servletPath.substring(1, servletPath.lastIndexOf('.'));
+               servletPath = servletPath.substring(servletPath.indexOf('/'), servletPath.length());
+               String path = URLDecoder.decode(servletPath, StandardCharsets.UTF_8);
+
+               boolean pdf = ".pdf".equals(ext);
+               ContentSession session = RemoteAuthUtils.doAs(() -> contentRepository.get(), new ServletHttpRequest(req));
+               Content content = session.get(path);
+               Source xmlInput = content.adapt(Source.class);
+               xmlInput.setSystemId(req.getRequestURI());
+
+               // dev only
+               try (InputStream in = new URL(xslUrl).openStream()) {
+                       Source xslSource = new StreamSource(in);
+                       foTemplates = transformerFactory.newTemplates(xslSource);
+                       if (foTemplates == null)
+                               throw new IllegalStateException("Could not instantiate XSL " + xslUrl);
+               } catch (TransformerConfigurationException | IOException e) {
+                       throw new IllegalStateException("Cannot instantiate XSL " + xslUrl, e);
+               }
+
+               URIResolver uriResolver = (href, base) -> {
+                       try {
+                               URL url = new URL(href);
+                               if (url.getProtocol().equals("file")) {
+                                       InputStream in = Files.newInputStream(Paths.get(URI.create(url.toString())));
+                                       return new StreamSource(in);
+                               }
+                       } catch (IOException e) {
+                               // silent
+                       }
+                       Content subContent = session.get(href);
+                       return subContent.adapt(Source.class);
+               };
+
+               ResourceResolver resourceResolver = new ResourceResolver() {
+
+                       @Override
+                       public Resource getResource(URI uri) throws IOException {
+                               String subPath = uri.getPath();
+                               Content subContent = session.get(subPath);
+                               InputStream in = subContent.open(InputStream.class);
+                               return new Resource(in);
+                       }
+
+                       @Override
+                       public OutputStream getOutputStream(URI uri) throws IOException {
+                               return null;
+                       }
+               };
+
+               try {
+                       if (pdf) {
+                               FopFactoryBuilder builder = new FopFactoryBuilder(URI.create(req.getRequestURL().toString()),
+                                               resourceResolver);
+                               FopFactory fopFactory = builder.build();
+//                             FopFactory fopFactory = FopFactory.newInstance(URI.create(req.getRequestURL().toString()));
+                               resp.setContentType("application/pdf");
+                               Fop fop = fopFactory.newFop("application/pdf", resp.getOutputStream());
+                               Transformer transformer = foTemplates.newTransformer();
+                               transformer.setURIResolver(uriResolver);
+                               Result fopResult = new SAXResult(fop.getDefaultHandler());
+                               transformer.transform(xmlInput, fopResult);
+
+                       } else {
+                               Result xmlOutput = new StreamResult(resp.getOutputStream());
+                               resp.setContentType("application/xml");
+                               Transformer transformer = foTemplates.newTransformer();
+//                             transformer = transformerFactory.newTransformer();// identity
+                               transformer.setURIResolver(uriResolver);
+                               transformer.transform(xmlInput, xmlOutput);
+                       }
+               } catch (FOPException | IOException | TransformerException e) {
+                       throw new RuntimeException("Cannot process " + path, e);
+               }
+
+       }
+
+       public void start(Map<String, Object> properties) {
+               documentBuilderFactory = DocumentBuilderFactory.newInstance();
+               documentBuilderFactory.setXIncludeAware(true);
+               documentBuilderFactory.setNamespaceAware(true);
+
+               // We must explicitly use the non-XSLTC transformer, as XSLTC is not working
+               // with DocBook stylesheets
+               transformerFactory = new TransformerFactoryImpl();
+//             transformerFactory = TransformerFactory.newDefaultInstance();
+
+               xslUrl = LangUtils.get(properties, "argeo.fo.xsl");
+               try (InputStream in = new URL(xslUrl).openStream()) {
+                       Source xslSource = new StreamSource(in);
+                       foTemplates = transformerFactory.newTemplates(xslSource);
+                       if (foTemplates == null)
+                               throw new IllegalStateException("Could not instantiate XSL " + xslUrl);
+               } catch (TransformerConfigurationException | IOException e) {
+                       throw new IllegalStateException("Cannot instantiate XSL " + xslUrl, e);
+               }
+       }
+
+       public void stop(Map<String, Object> properties) {
+
+       }
+
+       public void setContentRepository(ContentRepository contentRepository) {
+               this.contentRepository = contentRepository;
+       }
+
+}