1 package org
.argeo
.support
.odk
.servlet
;
3 import java
.io
.IOException
;
4 import java
.io
.OutputStream
;
6 import java
.nio
.charset
.Charset
;
7 import java
.security
.DigestOutputStream
;
8 import java
.security
.MessageDigest
;
9 import java
.security
.NoSuchAlgorithmException
;
10 import java
.util
.ArrayList
;
11 import java
.util
.List
;
13 import javax
.jcr
.ItemNotFoundException
;
14 import javax
.jcr
.Node
;
15 import javax
.jcr
.NodeIterator
;
16 import javax
.jcr
.Property
;
17 import javax
.jcr
.Repository
;
18 import javax
.jcr
.RepositoryException
;
19 import javax
.jcr
.Session
;
20 import javax
.jcr
.Value
;
21 import javax
.jcr
.nodetype
.NodeType
;
22 import javax
.jcr
.query
.Query
;
23 import javax
.jcr
.query
.QueryResult
;
24 import javax
.jcr
.query
.Row
;
25 import javax
.jcr
.query
.RowIterator
;
26 import javax
.servlet
.ServletException
;
27 import javax
.servlet
.http
.HttpServlet
;
28 import javax
.servlet
.http
.HttpServletRequest
;
29 import javax
.servlet
.http
.HttpServletResponse
;
31 import org
.apache
.commons
.io
.output
.NullOutputStream
;
32 import org
.argeo
.cms
.servlet
.ServletAuthUtils
;
33 import org
.argeo
.entity
.EntityMimeType
;
34 import org
.argeo
.jcr
.Jcr
;
35 import org
.argeo
.jcr
.JcrException
;
36 import org
.argeo
.support
.odk
.OrxManifestName
;
37 import org
.argeo
.util
.CsvWriter
;
38 import org
.argeo
.util
.DigestUtils
;
40 /** Describe additional files. */
41 public class OdkManifestServlet
extends HttpServlet
{
42 private static final long serialVersionUID
= 138030510865877478L;
44 private Repository repository
;
47 protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
48 resp
.setHeader("X-OpenRosa-Version", "1.0");
49 resp
.setDateHeader("Date", System
.currentTimeMillis());
51 String pathInfo
= req
.getPathInfo();
52 if (pathInfo
.startsWith("//"))
53 pathInfo
= pathInfo
.substring(1);
55 String serverName
= req
.getServerName();
56 int serverPort
= req
.getServerPort();
57 String protocol
= serverPort
== 443 || req
.isSecure() ?
"https" : "http";
59 Session session
= ServletAuthUtils
.doAs(() -> Jcr
.login(repository
, null), req
);
62 Node node
= session
.getNode(pathInfo
);
63 if (node
.isNodeType(OrxManifestName
.manifest
.get())) {
64 resp
.setContentType(EntityMimeType
.XML
.toHttpContentType());
65 Writer writer
= resp
.getWriter();
66 writer
.append("<?xml version='1.0' encoding='UTF-8' ?>");
67 writer
.append("<manifest xmlns=\"http://openrosa.org/xforms/xformsManifest\">");
68 NodeIterator nit
= node
.getNodes();
69 children
: while (nit
.hasNext()) {
70 Node file
= nit
.nextNode();
71 if (file
.isNodeType(OrxManifestName
.mediaFile
.get())) {
72 EntityMimeType mimeType
= EntityMimeType
73 .find(file
.getProperty(Property
.JCR_MIMETYPE
).getString());
74 Charset charset
= Charset
.forName(file
.getProperty(Property
.JCR_ENCODING
).getString());
76 if (file
.isNodeType(NodeType
.NT_ADDRESS
)) {
79 target
= file
.getProperty(Property
.JCR_ID
).getNode();
80 } catch (ItemNotFoundException e
) {
81 // TODO remove old manifests
84 writer
.append("<mediaFile>");
85 writer
.append("<filename>");
86 // Work around bug in ODK Collect not supporting paths
87 // writer.append(target.getPath().substring(1) + ".xml");
88 writer
.append(target
.getIdentifier() + "." + mimeType
.getDefaultExtension());
89 writer
.append("</filename>");
91 MessageDigest messageDigest
= MessageDigest
.getInstance(DigestUtils
.MD5
);
92 // TODO cache a temp file ?
93 try (DigestOutputStream out
= new DigestOutputStream(new NullOutputStream(),
95 writeMediaFile(out
, target
, mimeType
, charset
);
96 writer
.append("<hash>");
97 writer
.append("md5sum:" + DigestUtils
.encodeHexString(out
.getMessageDigest().digest()));
98 writer
.append("</hash>");
101 // try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
102 // session.exportDocumentView(target.getPath(), out, true, false);
103 // String fileCsum = DigestUtils.digest(DigestUtils.MD5, out.toByteArray());
104 // writer.append("<hash>");
105 // writer.append("md5sum:" + fileCsum);
106 // writer.append("</hash>");
108 writer
.append("<downloadUrl>" + protocol
+ "://" + serverName
109 + (serverPort
== 80 || serverPort
== 443 ?
"" : ":" + serverPort
)
110 + "/api/odk/formManifest" + file
.getPath() + "</downloadUrl>");
112 writer
.append("</mediaFile>");
116 writer
.append("</manifest>");
117 } else if (node
.isNodeType(OrxManifestName
.mediaFile
.get())) {
118 EntityMimeType mimeType
= EntityMimeType
.find(node
.getProperty(Property
.JCR_MIMETYPE
).getString());
119 Charset charset
= Charset
.forName(node
.getProperty(Property
.JCR_ENCODING
).getString());
120 resp
.setContentType(mimeType
.toHttpContentType(charset
));
121 if (node
.isNodeType(NodeType
.NT_ADDRESS
)) {
122 Node target
= node
.getProperty(Property
.JCR_ID
).getNode();
124 writeMediaFile(resp
.getOutputStream(), target
, mimeType
, charset
);
125 // try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
126 // session.exportDocumentView(target.getPath(), out, true, false);
127 // System.out.println(new String(out.toByteArray(), StandardCharsets.UTF_8));
128 // resp.getOutputStream().write(out.toByteArray());
131 throw new IllegalArgumentException("Unsupported node " + node
);
134 throw new IllegalArgumentException("Unsupported node " + node
);
136 } catch (RepositoryException e
) {
137 throw new JcrException(e
);
138 } catch (NoSuchAlgorithmException e
) {
139 throw new ServletException(e
);
146 protected void writeMediaFile(OutputStream out
, Node target
, EntityMimeType mimeType
, Charset charset
)
147 throws RepositoryException
, IOException
{
148 if (target
.isNodeType(NodeType
.NT_QUERY
)) {
149 Query query
= target
.getSession().getWorkspace().getQueryManager().getQuery(target
);
150 QueryResult queryResult
= query
.execute();
151 String
[] columnNames
= queryResult
.getColumnNames();
152 if (EntityMimeType
.XML
.equals(mimeType
)) {
153 } else if (EntityMimeType
.CSV
.equals(mimeType
)) {
154 CsvWriter csvWriter
= new CsvWriter(out
, charset
);
155 csvWriter
.writeLine(columnNames
);
156 RowIterator rit
= queryResult
.getRows();
157 while (rit
.hasNext()) {
158 Row row
= rit
.nextRow();
159 Value
[] values
= row
.getValues();
160 List
<String
> lst
= new ArrayList
<>();
161 for (Value value
: values
) {
162 lst
.add(value
.getString());
164 csvWriter
.writeLine(lst
);
168 if (EntityMimeType
.XML
.equals(mimeType
)) {
169 target
.getSession().exportDocumentView(target
.getPath(), out
, true, false);
170 } else if (EntityMimeType
.CSV
.equals(mimeType
)) {
171 CsvWriter csvWriter
= new CsvWriter(out
, charset
);
172 csvWriter
.writeLine(new String
[] { "name", "label" });
173 NodeIterator children
= target
.getNodes();
174 while (children
.hasNext()) {
175 Node child
= children
.nextNode();
176 String label
= Jcr
.getTitle(child
);
177 csvWriter
.writeLine(new String
[] { child
.getIdentifier(), label
});
185 public void setRepository(Repository repository
) {
186 this.repository
= repository
;