X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=blobdiff_plain;f=org.argeo.app.servlet.odk%2Fsrc%2Forg%2Fargeo%2Fapp%2Fservlet%2Fodk%2FOdkManifestServlet.java;fp=org.argeo.app.servlet.odk%2Fsrc%2Forg%2Fargeo%2Fapp%2Fservlet%2Fodk%2FOdkManifestServlet.java;h=f296170778a381e05cfbf61a52751ee8bb12839a;hp=0000000000000000000000000000000000000000;hb=6e56ffa34cb02ab04d028423aea342e3dfed4358;hpb=c285180bece610b2c2921d44fe14b6dde2123efa diff --git a/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkManifestServlet.java b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkManifestServlet.java new file mode 100644 index 0000000..f296170 --- /dev/null +++ b/org.argeo.app.servlet.odk/src/org/argeo/app/servlet/odk/OdkManifestServlet.java @@ -0,0 +1,185 @@ +package org.argeo.app.servlet.odk; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +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.app.api.EntityMimeType; +import org.argeo.app.odk.OrxManifestName; +import org.argeo.cms.auth.RemoteAuthUtils; +import org.argeo.cms.servlet.ServletHttpRequest; +import org.argeo.jcr.Jcr; +import org.argeo.jcr.JcrException; +import org.argeo.util.CsvWriter; +import org.argeo.util.DigestUtils; + +/** Describe additional files. */ +public class OdkManifestServlet extends HttpServlet { + private static final long serialVersionUID = 138030510865877478L; + + private Repository repository; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.setHeader("X-OpenRosa-Version", "1.0"); + resp.setDateHeader("Date", System.currentTimeMillis()); + + String pathInfo = req.getPathInfo(); + if (pathInfo.startsWith("//")) + pathInfo = pathInfo.substring(1); + + String serverName = req.getServerName(); + int serverPort = req.getServerPort(); + String protocol = serverPort == 443 || req.isSecure() ? "https" : "http"; + + Session session = RemoteAuthUtils.doAs(() -> Jcr.login(repository, null), new ServletHttpRequest(req)); + + try { + Node node = session.getNode(pathInfo); + if (node.isNodeType(OrxManifestName.manifest.get())) { + resp.setContentType(EntityMimeType.XML.toHttpContentType()); + Writer writer = resp.getWriter(); + writer.append(""); + writer.append(""); + NodeIterator nit = node.getNodes(); + children: while (nit.hasNext()) { + Node file = nit.nextNode(); + if (file.isNodeType(OrxManifestName.mediaFile.get())) { + 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; + try { + target = file.getProperty(Property.JCR_ID).getNode(); + } catch (ItemNotFoundException e) { + // TODO remove old manifests + continue children; + } + writer.append(""); + writer.append(""); + // Work around bug in ODK Collect not supporting paths + // writer.append(target.getPath().substring(1) + ".xml"); + writer.append(target.getIdentifier() + "." + mimeType.getDefaultExtension()); + writer.append(""); + + MessageDigest messageDigest = MessageDigest.getInstance(DigestUtils.MD5); + // TODO cache a temp file ? + try (DigestOutputStream out = new DigestOutputStream(NullOutputStream.NULL_OUTPUT_STREAM, + messageDigest)) { + writeMediaFile(out, target, mimeType, charset); + writer.append(""); + writer.append("md5sum:" + DigestUtils.toHexString(out.getMessageDigest().digest())); + writer.append(""); + } + writer.append("" + protocol + "://" + serverName + + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + + "/api/odk/formManifest" + file.getPath() + ""); + } + writer.append(""); + } + } + + writer.append(""); + } 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(); + + writeMediaFile(resp.getOutputStream(), target, mimeType, charset); + } else { + throw new IllegalArgumentException("Unsupported node " + node); + } + } else { + throw new IllegalArgumentException("Unsupported node " + node); + } + } 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(); + if (rit.hasNext()) { + while (rit.hasNext()) { + Row row = rit.nextRow(); + Value[] values = row.getValues(); + List lst = new ArrayList<>(); + for (Value value : values) { + lst.add(value.getString()); + } + csvWriter.writeLine(lst); + } + } else { + // corner case of an empty initial database + List lst = new ArrayList<>(); + for (int i = 0; i < columnNames.length; i++) + lst.add("-"); + 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; + } + +}