1 package org
.argeo
.publishing
.servlet
;
3 import java
.io
.ByteArrayInputStream
;
4 import java
.io
.ByteArrayOutputStream
;
5 import java
.io
.IOException
;
6 import java
.io
.InputStream
;
7 import java
.net
.URLDecoder
;
8 import java
.nio
.charset
.StandardCharsets
;
9 import java
.nio
.file
.Files
;
10 import java
.nio
.file
.Paths
;
11 import java
.util
.Collections
;
12 import java
.util
.HashMap
;
15 import javax
.jcr
.Node
;
16 import javax
.jcr
.Repository
;
17 import javax
.jcr
.RepositoryException
;
18 import javax
.jcr
.Session
;
19 import javax
.jcr
.nodetype
.NodeType
;
20 import javax
.servlet
.ServletException
;
21 import javax
.servlet
.http
.HttpServlet
;
22 import javax
.servlet
.http
.HttpServletRequest
;
23 import javax
.servlet
.http
.HttpServletResponse
;
24 import javax
.xml
.parsers
.DocumentBuilder
;
25 import javax
.xml
.parsers
.DocumentBuilderFactory
;
26 import javax
.xml
.transform
.Result
;
27 import javax
.xml
.transform
.Source
;
28 import javax
.xml
.transform
.Templates
;
29 import javax
.xml
.transform
.Transformer
;
30 import javax
.xml
.transform
.TransformerConfigurationException
;
31 import javax
.xml
.transform
.TransformerFactory
;
32 import javax
.xml
.transform
.dom
.DOMSource
;
33 import javax
.xml
.transform
.stream
.StreamResult
;
34 import javax
.xml
.transform
.stream
.StreamSource
;
36 import org
.apache
.commons
.io
.IOUtils
;
37 import org
.apache
.xalan
.processor
.TransformerFactoryImpl
;
38 import org
.argeo
.api
.cms
.CmsTheme
;
39 import org
.argeo
.cms
.servlet
.ServletAuthUtils
;
40 import org
.argeo
.cms
.servlet
.ServletHttpRequest
;
41 import org
.argeo
.docbook
.DbkType
;
42 import org
.argeo
.docbook
.DbkUtils
;
43 import org
.argeo
.jcr
.Jcr
;
44 import org
.argeo
.jcr
.JcrException
;
45 import org
.argeo
.jcr
.JcrUtils
;
46 import org
.w3c
.dom
.Document
;
49 * A servlet transforming a dbk:* JCR node into HTML, using the DocBook XSL.
51 public class DbkServlet
extends HttpServlet
{
52 private static final long serialVersionUID
= 6906020513498289335L;
54 private Repository repository
;
56 private DocumentBuilderFactory documentBuilderFactory
;
57 private TransformerFactory transformerFactory
;
58 private Templates docBoookTemplates
;
60 private Map
<String
, CmsTheme
> themes
= Collections
.synchronizedMap(new HashMap
<>());
63 protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
65 String pathInfo
= req
.getPathInfo();
66 if (pathInfo
.startsWith("//"))
67 pathInfo
= pathInfo
.substring(1);
68 String path
= URLDecoder
.decode(pathInfo
, StandardCharsets
.UTF_8
);
70 if (path
.toLowerCase().endsWith(".css")) {
71 path
= path
.substring(1);
72 int firstSlash
= path
.indexOf('/');
73 String themeId
= path
.substring(0, firstSlash
);
74 String cssPath
= path
.substring(firstSlash
);
75 CmsTheme cmsTheme
= themes
.get(themeId
);
77 throw new IllegalArgumentException("Theme " + themeId
+ " not found.");
78 resp
.setContentType("text/css");
79 IOUtils
.copy(cmsTheme
.getResourceAsStream(cssPath
), resp
.getOutputStream());
83 if (path
.toLowerCase().endsWith("/index.html")) {
84 path
= path
.substring(0, path
.length() - "/index.html".length());
87 Session session
= null;
89 session
= ServletAuthUtils
.doAs(() -> Jcr
.login(repository
, null), new ServletHttpRequest(req
));
90 Node node
= session
.getNode(path
);
92 if (node
.hasNode(DbkType
.article
.get())) {
93 Node dbkNode
= node
.getNode(DbkType
.article
.get());
94 if (DbkUtils
.isDbk(dbkNode
)) {
95 CmsTheme cmsTheme
= null;
96 String themeId
= req
.getParameter("themeId");
97 if (themeId
!= null) {
98 cmsTheme
= themes
.get(themeId
);
100 throw new IllegalArgumentException("Theme " + themeId
+ " not found.");
103 // TODO customise DocBook so that it outputs UTF-8
104 // see http://www.sagehill.net/docbookxsl/OutputEncoding.html
105 resp
.setContentType("text/html; charset=ISO-8859-1");
107 // TODO optimise with pipes, SAX, etc. ?
109 try (ByteArrayOutputStream out
= new ByteArrayOutputStream()) {
110 session
.exportDocumentView(dbkNode
.getPath(), out
, true, false);
111 arr
= out
.toByteArray();
112 // System.out.println(new String(arr, StandardCharsets.UTF_8));
113 } catch (RepositoryException e
) {
114 throw new JcrException(e
);
117 try (InputStream in
= new ByteArrayInputStream(arr
);
118 // ByteArrayOutputStream out = new ByteArrayOutputStream();
121 Result xmlOutput
= new StreamResult(resp
.getOutputStream());
123 DocumentBuilder docBuilder
= documentBuilderFactory
.newDocumentBuilder();
124 // Document doc = docBuilder.parse(new File(
125 // System.getProperty("user.home") + "/dev/git/gpl/argeo-qa/doc/platform/argeo-platform.dbk.xml"));
126 Document doc
= docBuilder
.parse(in
);
127 Source xmlInput
= new DOMSource(doc
);
129 Transformer transformer
= docBoookTemplates
.newTransformer();
132 if (cmsTheme
!= null) {
133 StringBuilder sb
= new StringBuilder();
134 for (String cssPath
: cmsTheme
.getWebCssPaths()) {
135 sb
.append(req
.getContextPath()).append(req
.getServletPath()).append('/');
136 sb
.append(themeId
).append('/').append(cssPath
).append(' ');
138 // FIXME make it more generic
139 sb
.append("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap")
142 "https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,300;0,400;0,600;1,400&display=swap")
145 transformer
.setParameter("html.stylesheet", sb
.toString());
147 transformer
.transform(xmlInput
, xmlOutput
);
148 // resp.getOutputStream().write(out.toByteArray());
149 } catch (Exception e
) {
150 throw new ServletException("Cannot transform " + path
, e
);
154 if (node
.isNodeType(NodeType
.NT_FILE
)) {// media download etc.
155 String fileNameLowerCase
= node
.getName().toLowerCase();
156 if (fileNameLowerCase
.endsWith(".jpg") || fileNameLowerCase
.endsWith(".jpeg")) {
157 resp
.setContentType("image/jpeg");
158 } else if (fileNameLowerCase
.endsWith(".png")) {
159 resp
.setContentType("image/png");
160 } else if (fileNameLowerCase
.endsWith(".gif")) {
161 resp
.setContentType("image/gif");
162 } else if (fileNameLowerCase
.endsWith(".svg")) {
163 resp
.setContentType("image/svg+xml");
165 // TODO know more content types...
166 resp
.setHeader("Content-Disposition", "attachment; filename=\"" + node
.getName() + "\"");
168 IOUtils
.copy(JcrUtils
.getFileAsStream(node
), resp
.getOutputStream());
170 throw new IllegalArgumentException("Unsupported node " + node
);
173 } catch (RepositoryException e1
) {
174 throw new JcrException(e1
);
181 public void init() throws ServletException
{
183 // TODO improve configuration and provisioning of DocBook XSL
184 String xslBase
= System
.getProperty("argeo.docbook.xsl");
185 if (xslBase
== null) {
186 String defaultXslBase
= "/opt/docbook-xsl";
187 if (!Files
.exists(Paths
.get(defaultXslBase
))) {
188 throw new ServletException("System property argeo.docbook.xsl is not set and default location "
189 + defaultXslBase
+ " does not exist.");
191 xslBase
= defaultXslBase
;
194 String xsl
= xslBase
+ "/html/docbook.xsl";
196 documentBuilderFactory
= DocumentBuilderFactory
.newInstance();
197 documentBuilderFactory
.setXIncludeAware(true);
198 documentBuilderFactory
.setNamespaceAware(true);
200 // We must explicitly use the non-XSLTC transformer, as XSLTC is not working
201 // with DocBook stylesheets
202 transformerFactory
= new TransformerFactoryImpl();
204 Source xslSource
= new StreamSource(xsl
);
206 docBoookTemplates
= transformerFactory
.newTemplates(xslSource
);
207 if (docBoookTemplates
== null)
208 throw new ServletException("Could not instantiate XSL " + xsl
);
209 } catch (TransformerConfigurationException e
) {
210 throw new ServletException("Cannot instantiate XSL " + xsl
, e
);
214 public void setRepository(Repository repository
) {
215 this.repository
= repository
;
218 public void addTheme(CmsTheme theme
, Map
<String
, String
> properties
) {
219 themes
.put(theme
.getThemeId(), theme
);
222 public void removeTheme(CmsTheme theme
, Map
<String
, String
> properties
) {
223 themes
.remove(theme
.getThemeId());