From 8e76163658cc6275b0ca4206f6ea4b3eee09d787 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Tue, 17 May 2022 13:55:14 +0200 Subject: [PATCH] Introduce FOP servlet. --- .../argeo/app/servlet/publish/FopServlet.java | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java 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 index 0000000..bba0d30 --- /dev/null +++ b/org.argeo.app.servlet.publish/src/org/argeo/app/servlet/publish/FopServlet.java @@ -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 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 properties) { + + } + + public void setContentRepository(ContentRepository contentRepository) { + this.contentRepository = contentRepository; + } + +} -- 2.30.2