1 package org
.argeo
.app
.servlet
.odk
;
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
.app
.api
.EntityMimeType
;
33 import org
.argeo
.app
.odk
.OrxManifestName
;
34 import org
.argeo
.cms
.auth
.RemoteAuthUtils
;
35 import org
.argeo
.cms
.servlet
.ServletHttpRequest
;
36 import org
.argeo
.jcr
.Jcr
;
37 import org
.argeo
.jcr
.JcrException
;
38 import org
.argeo
.util
.CsvWriter
;
39 import org
.argeo
.util
.DigestUtils
;
41 /** Describe additional files. */
42 public class OdkManifestServlet
extends HttpServlet
{
43 private static final long serialVersionUID
= 138030510865877478L;
45 private Repository repository
;
48 protected void doGet(HttpServletRequest req
, HttpServletResponse resp
) throws ServletException
, IOException
{
49 resp
.setHeader("X-OpenRosa-Version", "1.0");
50 resp
.setDateHeader("Date", System
.currentTimeMillis());
52 String pathInfo
= req
.getPathInfo();
53 if (pathInfo
.startsWith("//"))
54 pathInfo
= pathInfo
.substring(1);
56 String serverName
= req
.getServerName();
57 int serverPort
= req
.getServerPort();
58 String protocol
= serverPort
== 443 || req
.isSecure() ?
"https" : "http";
60 Session session
= RemoteAuthUtils
.doAs(() -> Jcr
.login(repository
, null), new ServletHttpRequest(req
));
63 Node node
= session
.getNode(pathInfo
);
64 if (node
.isNodeType(OrxManifestName
.manifest
.get())) {
65 resp
.setContentType(EntityMimeType
.XML
.toHttpContentType());
66 Writer writer
= resp
.getWriter();
67 writer
.append("<?xml version='1.0' encoding='UTF-8' ?>");
68 writer
.append("<manifest xmlns=\"http://openrosa.org/xforms/xformsManifest\">");
69 NodeIterator nit
= node
.getNodes();
70 children
: while (nit
.hasNext()) {
71 Node file
= nit
.nextNode();
72 if (file
.isNodeType(OrxManifestName
.mediaFile
.get())) {
73 EntityMimeType mimeType
= EntityMimeType
74 .find(file
.getProperty(Property
.JCR_MIMETYPE
).getString());
75 Charset charset
= Charset
.forName(file
.getProperty(Property
.JCR_ENCODING
).getString());
77 if (file
.isNodeType(NodeType
.NT_ADDRESS
)) {
80 target
= file
.getProperty(Property
.JCR_ID
).getNode();
81 } catch (ItemNotFoundException e
) {
82 // TODO remove old manifests
85 writer
.append("<mediaFile>");
86 writer
.append("<filename>");
87 // Work around bug in ODK Collect not supporting paths
88 // writer.append(target.getPath().substring(1) + ".xml");
89 writer
.append(target
.getIdentifier() + "." + mimeType
.getDefaultExtension());
90 writer
.append("</filename>");
92 MessageDigest messageDigest
= MessageDigest
.getInstance(DigestUtils
.MD5
);
93 // TODO cache a temp file ?
94 try (DigestOutputStream out
= new DigestOutputStream(NullOutputStream
.NULL_OUTPUT_STREAM
,
96 writeMediaFile(out
, target
, mimeType
, charset
);
97 writer
.append("<hash>");
98 writer
.append("md5sum:" + DigestUtils
.toHexString(out
.getMessageDigest().digest()));
99 writer
.append("</hash>");
101 writer
.append("<downloadUrl>" + protocol
+ "://" + serverName
102 + (serverPort
== 80 || serverPort
== 443 ?
"" : ":" + serverPort
)
103 + "/api/odk/formManifest" + file
.getPath() + "</downloadUrl>");
105 writer
.append("</mediaFile>");
109 writer
.append("</manifest>");
110 } else if (node
.isNodeType(OrxManifestName
.mediaFile
.get())) {
111 EntityMimeType mimeType
= EntityMimeType
.find(node
.getProperty(Property
.JCR_MIMETYPE
).getString());
112 Charset charset
= Charset
.forName(node
.getProperty(Property
.JCR_ENCODING
).getString());
113 resp
.setContentType(mimeType
.toHttpContentType(charset
));
114 if (node
.isNodeType(NodeType
.NT_ADDRESS
)) {
115 Node target
= node
.getProperty(Property
.JCR_ID
).getNode();
117 writeMediaFile(resp
.getOutputStream(), target
, mimeType
, charset
);
119 throw new IllegalArgumentException("Unsupported node " + node
);
122 throw new IllegalArgumentException("Unsupported node " + node
);
124 } catch (RepositoryException e
) {
125 throw new JcrException(e
);
126 } catch (NoSuchAlgorithmException e
) {
127 throw new ServletException(e
);
134 protected void writeMediaFile(OutputStream out
, Node target
, EntityMimeType mimeType
, Charset charset
)
135 throws RepositoryException
, IOException
{
136 if (target
.isNodeType(NodeType
.NT_QUERY
)) {
137 Query query
= target
.getSession().getWorkspace().getQueryManager().getQuery(target
);
138 QueryResult queryResult
= query
.execute();
139 String
[] columnNames
= queryResult
.getColumnNames();
140 if (EntityMimeType
.XML
.equals(mimeType
)) {
141 } else if (EntityMimeType
.CSV
.equals(mimeType
)) {
142 CsvWriter csvWriter
= new CsvWriter(out
, charset
);
143 csvWriter
.writeLine(columnNames
);
144 RowIterator rit
= queryResult
.getRows();
146 while (rit
.hasNext()) {
147 Row row
= rit
.nextRow();
148 Value
[] values
= row
.getValues();
149 List
<String
> lst
= new ArrayList
<>();
150 for (Value value
: values
) {
151 lst
.add(value
.getString());
153 csvWriter
.writeLine(lst
);
156 // corner case of an empty initial database
157 List
<String
> lst
= new ArrayList
<>();
158 for (int i
= 0; i
< columnNames
.length
; i
++)
160 csvWriter
.writeLine(lst
);
164 if (EntityMimeType
.XML
.equals(mimeType
)) {
165 target
.getSession().exportDocumentView(target
.getPath(), out
, true, false);
166 } else if (EntityMimeType
.CSV
.equals(mimeType
)) {
167 CsvWriter csvWriter
= new CsvWriter(out
, charset
);
168 csvWriter
.writeLine(new String
[] { "name", "label" });
169 NodeIterator children
= target
.getNodes();
170 while (children
.hasNext()) {
171 Node child
= children
.nextNode();
172 String label
= Jcr
.getTitle(child
);
173 csvWriter
.writeLine(new String
[] { child
.getIdentifier(), label
});
181 public void setRepository(Repository repository
) {
182 this.repository
= repository
;