Dynamic secondary instances based on queries, and supporting CSV in
[gpl/argeo-suite.git] / knowledge / org.argeo.support.odk / src / org / argeo / support / odk / servlet / OdkManifestServlet.java
index 091f4dabfc7340cda6b95ad4092a7918572890ec..3510e06ee154b50e7426c38773f01fe8107cd15c 100644 (file)
@@ -1,26 +1,40 @@
 package org.argeo.support.odk.servlet;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.Writer;
-import java.nio.charset.StandardCharsets;
-
+import java.nio.charset.Charset;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.Property;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.Value;
 import javax.jcr.nodetype.NodeType;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.io.output.NullOutputStream;
 import org.argeo.cms.servlet.ServletAuthUtils;
+import org.argeo.entity.EntityMimeType;
 import org.argeo.jcr.Jcr;
 import org.argeo.jcr.JcrException;
 import org.argeo.support.odk.OrxManifestName;
+import org.argeo.util.CsvWriter;
 import org.argeo.util.DigestUtils;
 
 /** Describe additional files. */
@@ -31,7 +45,6 @@ public class OdkManifestServlet extends HttpServlet {
 
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-               resp.setContentType("text/xml; charset=utf-8");
                resp.setHeader("X-OpenRosa-Version", "1.0");
                resp.setDateHeader("Date", System.currentTimeMillis());
 
@@ -48,40 +61,50 @@ public class OdkManifestServlet extends HttpServlet {
                try {
                        Node node = session.getNode(pathInfo);
                        if (node.isNodeType(OrxManifestName.manifest.get())) {
+                               resp.setContentType(EntityMimeType.XML.toHttpContentType());
                                Writer writer = resp.getWriter();
                                writer.append("<?xml version='1.0' encoding='UTF-8' ?>");
                                writer.append("<manifest xmlns=\"http://openrosa.org/xforms/xformsManifest\">");
                                NodeIterator nit = node.getNodes();
-                               while (nit.hasNext()) {
+                               children: while (nit.hasNext()) {
                                        Node file = nit.nextNode();
                                        if (file.isNodeType(OrxManifestName.mediaFile.get())) {
-                                               writer.append("<mediaFile>");
+                                               EntityMimeType mimeType = EntityMimeType
+                                                               .find(file.getProperty(Property.JCR_MIMETYPE).getString());
+                                               Charset charset = Charset.forName(file.getProperty(Property.JCR_ENCODING).getString());
 
                                                if (file.isNodeType(NodeType.NT_ADDRESS)) {
-                                                       Node target = file.getProperty(Property.JCR_ID).getNode();
+                                                       Node target;
+                                                       try {
+                                                               target = file.getProperty(Property.JCR_ID).getNode();
+                                                       } catch (ItemNotFoundException e) {
+                                                               // TODO remove old manifests
+                                                               continue children;
+                                                       }
+                                                       writer.append("<mediaFile>");
                                                        writer.append("<filename>");
                                                        // Work around bug in ODK Collect not supporting paths
                                                        // writer.append(target.getPath().substring(1) + ".xml");
-                                                       writer.append(target.getIdentifier() + ".xml");
+                                                       writer.append(target.getIdentifier() + "." + mimeType.getDefaultExtension());
                                                        writer.append("</filename>");
 
-//                                                     StringBuilder xml = new StringBuilder();
-//                                                     xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
-//                                                     JcrUtils.toSimpleXml(target, xml);
-//                                                     String fileCsum = DigestUtils.digest(DigestUtils.MD5,
-//                                                                     xml.toString().getBytes(StandardCharsets.UTF_8));
-//                                                     writer.append("<hash>");
-//                                                     writer.append("md5sum:" + fileCsum);
-//                                                     writer.append("</hash>");
-
-                                                       try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-                                                               session.exportDocumentView(target.getPath(), out, true, false);
-                                                               String fileCsum = DigestUtils.digest(DigestUtils.MD5, out.toByteArray());
-//                                             JcrxApi.addChecksum(file, fileCsum);
+                                                       MessageDigest messageDigest = MessageDigest.getInstance(DigestUtils.MD5);
+                                                       // TODO cache a temp file ?
+                                                       try (DigestOutputStream out = new DigestOutputStream(new NullOutputStream(),
+                                                                       messageDigest)) {
+                                                               writeMediaFile(out, target, mimeType, charset);
                                                                writer.append("<hash>");
-                                                               writer.append("md5sum:" + fileCsum);
+                                                               writer.append("md5sum:" + DigestUtils.encodeHexString(out.getMessageDigest().digest()));
                                                                writer.append("</hash>");
                                                        }
+
+//                                                     try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+//                                                             session.exportDocumentView(target.getPath(), out, true, false);
+//                                                             String fileCsum = DigestUtils.digest(DigestUtils.MD5, out.toByteArray());
+//                                                             writer.append("<hash>");
+//                                                             writer.append("md5sum:" + fileCsum);
+//                                                             writer.append("</hash>");
+//                                                     }
                                                        writer.append("<downloadUrl>" + protocol + "://" + serverName
                                                                        + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort)
                                                                        + "/api/odk/formManifest" + file.getPath() + "</downloadUrl>");
@@ -92,21 +115,18 @@ public class OdkManifestServlet extends HttpServlet {
 
                                writer.append("</manifest>");
                        } else if (node.isNodeType(OrxManifestName.mediaFile.get())) {
+                               EntityMimeType mimeType = EntityMimeType.find(node.getProperty(Property.JCR_MIMETYPE).getString());
+                               Charset charset = Charset.forName(node.getProperty(Property.JCR_ENCODING).getString());
+                               resp.setContentType(mimeType.toHttpContentType(charset));
                                if (node.isNodeType(NodeType.NT_ADDRESS)) {
                                        Node target = node.getProperty(Property.JCR_ID).getNode();
 
-//                                     StringBuilder xml = new StringBuilder();
-//                                     xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
-//                                     JcrUtils.toSimpleXml(target, xml);
-//                                     System.out.println(xml);
-//                                     resp.getOutputStream().write(xml.toString().getBytes(StandardCharsets.UTF_8));
-//                                     resp.flushBuffer();
-
-                                       try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-                                               session.exportDocumentView(target.getPath(), out, true, false);
-                                               System.out.println(new String(out.toByteArray(), StandardCharsets.UTF_8));
-                                               resp.getOutputStream().write(out.toByteArray());
-                                       }
+                                       writeMediaFile(resp.getOutputStream(), target, mimeType, charset);
+//                                     try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+//                                             session.exportDocumentView(target.getPath(), out, true, false);
+//                                             System.out.println(new String(out.toByteArray(), StandardCharsets.UTF_8));
+//                                             resp.getOutputStream().write(out.toByteArray());
+//                                     }
                                } else {
                                        throw new IllegalArgumentException("Unsupported node " + node);
                                }
@@ -115,12 +135,53 @@ public class OdkManifestServlet extends HttpServlet {
                        }
                } catch (RepositoryException e) {
                        throw new JcrException(e);
+               } catch (NoSuchAlgorithmException e) {
+                       throw new ServletException(e);
                } finally {
                        Jcr.logout(session);
                }
 
        }
 
+       protected void writeMediaFile(OutputStream out, Node target, EntityMimeType mimeType, Charset charset)
+                       throws RepositoryException, IOException {
+               if (target.isNodeType(NodeType.NT_QUERY)) {
+                       Query query = target.getSession().getWorkspace().getQueryManager().getQuery(target);
+                       QueryResult queryResult = query.execute();
+                       String[] columnNames = queryResult.getColumnNames();
+                       if (EntityMimeType.XML.equals(mimeType)) {
+                       } else if (EntityMimeType.CSV.equals(mimeType)) {
+                               CsvWriter csvWriter = new CsvWriter(out, charset);
+                               csvWriter.writeLine(columnNames);
+                               RowIterator rit = queryResult.getRows();
+                               while (rit.hasNext()) {
+                                       Row row = rit.nextRow();
+                                       Value[] values = row.getValues();
+                                       List<String> lst = new ArrayList<>();
+                                       for (Value value : values) {
+                                               lst.add(value.getString());
+                                       }
+                                       csvWriter.writeLine(lst);
+                               }
+                       }
+               } else {
+                       if (EntityMimeType.XML.equals(mimeType)) {
+                               target.getSession().exportDocumentView(target.getPath(), out, true, false);
+                       } else if (EntityMimeType.CSV.equals(mimeType)) {
+                               CsvWriter csvWriter = new CsvWriter(out, charset);
+                               csvWriter.writeLine(new String[] { "name", "label" });
+                               NodeIterator children = target.getNodes();
+                               while (children.hasNext()) {
+                                       Node child = children.nextNode();
+                                       String label = Jcr.getTitle(child);
+                                       csvWriter.writeLine(new String[] { child.getIdentifier(), label });
+                               }
+                       }
+
+               }
+
+       }
+
        public void setRepository(Repository repository) {
                this.repository = repository;
        }