]> git.argeo.org Git - gpl/argeo-suite.git/blob - org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkManifestServlet.java
UI time out working
[gpl/argeo-suite.git] / org.argeo.app.servlet.odk / src / org / argeo / app / servlet / odk / OdkManifestServlet.java
1 package org.argeo.app.servlet.odk;
2
3 import java.io.IOException;
4 import java.io.OutputStream;
5 import java.io.Writer;
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;
12
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;
30
31 import org.apache.commons.io.output.NullOutputStream;
32 import org.argeo.app.api.EntityMimeType;
33 import org.argeo.app.api.EntityType;
34 import org.argeo.app.api.WGS84PosName;
35 import org.argeo.app.geo.GeoShapeUtils;
36 import org.argeo.app.odk.OrxManifestName;
37 import org.argeo.cms.auth.RemoteAuthUtils;
38 import org.argeo.cms.servlet.ServletHttpRequest;
39 import org.argeo.cms.servlet.ServletUtils;
40 import org.argeo.cms.util.CsvWriter;
41 import org.argeo.cms.util.DigestUtils;
42 import org.argeo.jcr.Jcr;
43 import org.argeo.jcr.JcrException;
44
45 /** Describe additional files. */
46 public class OdkManifestServlet extends HttpServlet {
47 private static final long serialVersionUID = 138030510865877478L;
48
49 private Repository repository;
50
51 @Override
52 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
53 resp.setHeader("X-OpenRosa-Version", "1.0");
54 resp.setDateHeader("Date", System.currentTimeMillis());
55
56 String pathInfo = req.getPathInfo();
57 if (pathInfo.startsWith("//"))
58 pathInfo = pathInfo.substring(1);
59
60 // we force HTTPS since ODK Collect will fail anyhow when sending http
61 // cf. https://forum.getodk.org/t/authentication-for-non-https-schems/32967/4
62 StringBuilder baseServer = ServletUtils.getRequestUrlBase(req, true);
63
64 Session session = RemoteAuthUtils.doAs(() -> Jcr.login(repository, null), new ServletHttpRequest(req));
65
66 try {
67 Node node = session.getNode(pathInfo);
68 if (node.isNodeType(OrxManifestName.manifest.get())) {
69 resp.setContentType(EntityMimeType.XML.toHttpContentType());
70 Writer writer = resp.getWriter();
71 writer.append("<?xml version='1.0' encoding='UTF-8' ?>");
72 writer.append("<manifest xmlns=\"http://openrosa.org/xforms/xformsManifest\">");
73 NodeIterator nit = node.getNodes();
74 children: while (nit.hasNext()) {
75 Node file = nit.nextNode();
76 if (file.isNodeType(OrxManifestName.mediaFile.get())) {
77 EntityMimeType mimeType = EntityMimeType
78 .find(file.getProperty(Property.JCR_MIMETYPE).getString());
79 Charset charset = Charset.forName(file.getProperty(Property.JCR_ENCODING).getString());
80
81 if (file.isNodeType(NodeType.NT_ADDRESS)) {
82 Node target;
83 try {
84 target = file.getProperty(Property.JCR_ID).getNode();
85 } catch (ItemNotFoundException e) {
86 // TODO remove old manifests
87 continue children;
88 }
89 writer.append("<mediaFile>");
90 writer.append("<filename>");
91 // Work around bug in ODK Collect not supporting paths
92 // writer.append(target.getPath().substring(1) + ".xml");
93 writer.append(target.getIdentifier() + "." + mimeType.getDefaultExtension());
94 writer.append("</filename>");
95
96 MessageDigest messageDigest = MessageDigest.getInstance(DigestUtils.MD5);
97 // TODO cache a temp file ?
98 try (DigestOutputStream out = new DigestOutputStream(NullOutputStream.NULL_OUTPUT_STREAM,
99 messageDigest)) {
100 writeMediaFile(out, target, mimeType, charset);
101 writer.append("<hash>");
102 writer.append("md5sum:" + DigestUtils.toHexString(out.getMessageDigest().digest()));
103 writer.append("</hash>");
104 }
105 writer.append("<downloadUrl>" + baseServer + "/api/odk/formManifest" + file.getPath()
106 + "</downloadUrl>");
107 }
108 writer.append("</mediaFile>");
109 }
110 }
111
112 writer.append("</manifest>");
113 } else if (node.isNodeType(OrxManifestName.mediaFile.get())) {
114 EntityMimeType mimeType = EntityMimeType.find(node.getProperty(Property.JCR_MIMETYPE).getString());
115 Charset charset = Charset.forName(node.getProperty(Property.JCR_ENCODING).getString());
116 resp.setContentType(mimeType.toHttpContentType(charset));
117 if (node.isNodeType(NodeType.NT_ADDRESS)) {
118 Node target = node.getProperty(Property.JCR_ID).getNode();
119
120 writeMediaFile(resp.getOutputStream(), target, mimeType, charset);
121 } else {
122 throw new IllegalArgumentException("Unsupported node " + node);
123 }
124 } else {
125 throw new IllegalArgumentException("Unsupported node " + node);
126 }
127 } catch (RepositoryException e) {
128 throw new JcrException(e);
129 } catch (NoSuchAlgorithmException e) {
130 throw new ServletException(e);
131 } finally {
132 Jcr.logout(session);
133 }
134
135 }
136
137 protected void writeMediaFile(OutputStream out, Node target, EntityMimeType mimeType, Charset charset)
138 throws RepositoryException, IOException {
139 if (target.isNodeType(NodeType.NT_QUERY)) {
140 Query query = target.getSession().getWorkspace().getQueryManager().getQuery(target);
141 QueryResult queryResult = query.execute();
142 List<String> columnNames = new ArrayList<>();
143 for (String c : queryResult.getColumnNames()) {
144 columnNames.add(c);
145 }
146 // TODO make it more configurable
147 columnNames.add("display");
148 columnNames.add("geometry");
149
150 if (EntityMimeType.XML.equals(mimeType)) {
151 } else if (EntityMimeType.CSV.equals(mimeType)) {
152 CsvWriter csvWriter = new CsvWriter(out, charset);
153 csvWriter.writeLine(columnNames);
154 RowIterator rit = queryResult.getRows();
155 if (rit.hasNext()) {
156 while (rit.hasNext()) {
157 Row row = rit.nextRow();
158 Value[] values = row.getValues();
159 List<String> lst = new ArrayList<>();
160 for (Value value : values) {
161 lst.add(value.getString());
162 }
163 // display
164 lst.add(row.getValue("name").getString() + " (" + row.getValue("label").getString() + ")");
165 Node field = row.getNode("geopoint");
166 if (field != null && field.isNodeType(EntityType.geopoint.get())) {
167 double lat = field.getProperty(WGS84PosName.lat.get()).getDouble();
168 double lon = field.getProperty(WGS84PosName.lon.get()).getDouble();
169 double alt = field.hasProperty(WGS84PosName.alt.get())
170 ? field.getProperty(WGS84PosName.alt.get()).getDouble()
171 : Double.NaN;
172 String geoshape = GeoShapeUtils.geoPointToGeoShape(lon, lat, alt);
173 lst.add(geoshape);
174 }
175 csvWriter.writeLine(lst);
176 }
177 } else {
178 // corner case of an empty initial database
179 List<String> lst = new ArrayList<>();
180 for (int i = 0; i < columnNames.size(); i++)
181 lst.add("-");
182 csvWriter.writeLine(lst);
183 }
184 }
185 } else {
186 if (EntityMimeType.XML.equals(mimeType)) {
187 target.getSession().exportDocumentView(target.getPath(), out, true, false);
188 } else if (EntityMimeType.CSV.equals(mimeType)) {
189 CsvWriter csvWriter = new CsvWriter(out, charset);
190 csvWriter.writeLine(new String[] { "name", "label" });
191 NodeIterator children = target.getNodes();
192 while (children.hasNext()) {
193 Node child = children.nextNode();
194 String label = Jcr.getTitle(child);
195 csvWriter.writeLine(new String[] { child.getIdentifier(), label });
196 }
197 }
198
199 }
200
201 }
202
203 public void setRepository(Repository repository) {
204 this.repository = repository;
205 }
206
207 }