From: Mathieu Baudier Date: Thu, 15 Oct 2020 09:25:33 +0000 (+0200) Subject: Introduce ODK forms support. X-Git-Tag: argeo-suite-2.1.16~68 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-suite.git;a=commitdiff_plain;h=6d2f48507e5fbe512dca68726c7ba8529764ae0b Introduce ODK forms support. --- diff --git a/dep/org.argeo.suite.dep.ui.rap/pom.xml b/dep/org.argeo.suite.dep.ui.rap/pom.xml index bac0d06..66858c2 100644 --- a/dep/org.argeo.suite.dep.ui.rap/pom.xml +++ b/dep/org.argeo.suite.dep.ui.rap/pom.xml @@ -51,6 +51,13 @@ org.argeo.suite.ui.rap 2.1.16-SNAPSHOT + + + + org.argeo.suite + org.argeo.support.odk + 2.1.16-SNAPSHOT + diff --git a/knowledge/org.argeo.support.odk/.classpath b/knowledge/org.argeo.support.odk/.classpath new file mode 100644 index 0000000..e801ebf --- /dev/null +++ b/knowledge/org.argeo.support.odk/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/knowledge/org.argeo.support.odk/.gitignore b/knowledge/org.argeo.support.odk/.gitignore new file mode 100644 index 0000000..09e3bc9 --- /dev/null +++ b/knowledge/org.argeo.support.odk/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/target/ diff --git a/knowledge/org.argeo.support.odk/.project b/knowledge/org.argeo.support.odk/.project new file mode 100644 index 0000000..ac46107 --- /dev/null +++ b/knowledge/org.argeo.support.odk/.project @@ -0,0 +1,33 @@ + + + org.argeo.support.odk + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/knowledge/org.argeo.support.odk/META-INF/.gitignore b/knowledge/org.argeo.support.odk/META-INF/.gitignore new file mode 100644 index 0000000..4854a41 --- /dev/null +++ b/knowledge/org.argeo.support.odk/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/knowledge/org.argeo.support.odk/OSGI-INF/odkFormListServlet.xml b/knowledge/org.argeo.support.odk/OSGI-INF/odkFormListServlet.xml new file mode 100644 index 0000000..eebd14e --- /dev/null +++ b/knowledge/org.argeo.support.odk/OSGI-INF/odkFormListServlet.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/knowledge/org.argeo.support.odk/OSGI-INF/odkFormServlet.xml b/knowledge/org.argeo.support.odk/OSGI-INF/odkFormServlet.xml new file mode 100644 index 0000000..c2cd89b --- /dev/null +++ b/knowledge/org.argeo.support.odk/OSGI-INF/odkFormServlet.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/knowledge/org.argeo.support.odk/OSGI-INF/odkSubmissionServlet.xml b/knowledge/org.argeo.support.odk/OSGI-INF/odkSubmissionServlet.xml new file mode 100644 index 0000000..77fba54 --- /dev/null +++ b/knowledge/org.argeo.support.odk/OSGI-INF/odkSubmissionServlet.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/knowledge/org.argeo.support.odk/bnd.bnd b/knowledge/org.argeo.support.odk/bnd.bnd new file mode 100644 index 0000000..9291447 --- /dev/null +++ b/knowledge/org.argeo.support.odk/bnd.bnd @@ -0,0 +1,5 @@ + +Service-Component:\ +OSGI-INF/odkFormListServlet.xml,\ +OSGI-INF/odkFormServlet.xml,\ +OSGI-INF/odkSubmissionServlet.xml diff --git a/knowledge/org.argeo.support.odk/build.properties b/knowledge/org.argeo.support.odk/build.properties new file mode 100644 index 0000000..6210e84 --- /dev/null +++ b/knowledge/org.argeo.support.odk/build.properties @@ -0,0 +1,5 @@ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/ +source.. = src/ diff --git a/knowledge/org.argeo.support.odk/pom.xml b/knowledge/org.argeo.support.odk/pom.xml new file mode 100644 index 0000000..ec62f62 --- /dev/null +++ b/knowledge/org.argeo.support.odk/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + org.argeo.suite + knowledge + 2.1.16-SNAPSHOT + .. + + org.argeo.support.odk + ODK support + jar + + + org.argeo.commons + org.argeo.util + ${version.argeo-commons} + + + diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/BundleResourceOdkForm.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/BundleResourceOdkForm.java new file mode 100644 index 0000000..fc55f35 --- /dev/null +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/BundleResourceOdkForm.java @@ -0,0 +1,101 @@ +package org.argeo.support.odk; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Map; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.argeo.util.DigestUtils; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +/** {@link OdkForm} implementation based on an OSGi {@link Bundle} resource. */ +public class BundleResourceOdkForm implements OdkForm { + private String formId; + private String name; + private String version; + private String description; + private String hash; + private String fileName; + + private byte[] data; + + public void init(Map properties, BundleContext bundleContext) throws IOException { + String location = properties.get("location"); + fileName = FilenameUtils.getName(location); + URL url = bundleContext.getBundle().getResource(location); + data = IOUtils.toByteArray(url.openStream()); + hash = "md5:" + DigestUtils.digest(DigestUtils.MD5, data); + + // TODO get it from the XML + formId = properties.get("formId"); + version = properties.get("version"); + + name = properties.get("name"); + description = properties.get("description"); + } + + @Override + public String getFormId() { + return formId; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getHash(String hashType) { + return hash; + } + + @Override + public String getFileName() { + return fileName; + } + + @Override + public InputStream openStream() { + return new ByteArrayInputStream(data); + } + + @Override + public int hashCode() { + assert formId != null; + assert version != null; + return formId.hashCode() + version.hashCode(); + } + + @Override + public boolean equals(Object obj) { + assert formId != null; + assert version != null; + if (!(obj instanceof OdkForm)) + return false; + OdkForm other = (OdkForm) obj; + assert other.getFormId() != null; + assert other.getVersion() != null; + + return other.getFormId().equals(formId) && other.getVersion().equals(version); + } + + @Override + public String toString() { + return "ODK Form " + formId + ", v" + version; + } + +} diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OdkForm.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OdkForm.java new file mode 100644 index 0000000..7b79a13 --- /dev/null +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OdkForm.java @@ -0,0 +1,20 @@ +package org.argeo.support.odk; + +import java.io.InputStream; + +/** Abstraction of a single ODK form. */ +public interface OdkForm { + String getFormId(); + + String getName(); + + String getVersion(); + + String getDescription(); + + String getHash(String hashType); + + String getFileName(); + + InputStream openStream(); +} diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormListServlet.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormListServlet.java new file mode 100644 index 0000000..8398c7f --- /dev/null +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormListServlet.java @@ -0,0 +1,66 @@ +package org.argeo.support.odk.servlet; + +import java.io.IOException; +import java.io.Writer; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.support.odk.OdkForm; + +/** Lists available forms. */ +public class OdkFormListServlet extends HttpServlet { + private static final long serialVersionUID = 2706191315048423321L; + private final static Log log = LogFactory.getLog(OdkFormListServlet.class); + + private Set odkForms = Collections.synchronizedSet(new HashSet<>()); + + @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()); + + String serverName = req.getServerName(); + int serverPort = req.getServerPort(); + String protocol = serverPort == 443 || req.isSecure() ? "https" : "http"; + + Writer writer = resp.getWriter(); + writer.append(""); + writer.append(""); + for (OdkForm form : odkForms) { + StringBuilder sb = new StringBuilder(); + sb.append(""); + sb.append("" + form.getFormId() + ""); + sb.append("" + form.getName() + ""); + sb.append("" + form.getVersion() + ""); + sb.append("" + form.getHash(null) + ""); + sb.append("" + form.getDescription() + ""); + sb.append("" + protocol + "://" + serverName + + (serverPort == 80 + || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/" + form.getFileName() + + ""); + sb.append(""); + String str = sb.toString(); + if (log.isDebugEnabled()) + log.debug(str); + writer.append(str); + } + writer.append(""); + } + + public void addForm(OdkForm odkForm) { + odkForms.add(odkForm); + } + + public void removeForm(OdkForm odkForm) { + odkForms.remove(odkForm); + } +} diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormServlet.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormServlet.java new file mode 100644 index 0000000..3746da8 --- /dev/null +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormServlet.java @@ -0,0 +1,50 @@ +package org.argeo.support.odk.servlet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.FilenameUtils; +import org.argeo.support.odk.OdkForm; + +/** Retrieves a single form. */ +public class OdkFormServlet extends HttpServlet { + private static final long serialVersionUID = 7838305967987687370L; + + private Map odkForms = Collections.synchronizedMap(new HashMap<>()); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.setContentType("text/xml; charset=utf-8"); + + String path = req.getServletPath(); + String fileName = FilenameUtils.getName(path); + OdkForm form = odkForms.get(fileName); + if (form == null) + throw new IllegalArgumentException("No form named " + fileName + " was found"); + + byte[] buffer = new byte[1024]; + try (InputStream in = form.openStream(); OutputStream out = resp.getOutputStream();) { + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) + out.write(buffer, 0, bytesRead); + } + } + + public void addForm(OdkForm odkForm) { + odkForms.put(odkForm.getFileName(), odkForm); + } + + public void removeForm(OdkForm odkForm) { + odkForms.remove(odkForm.getFileName()); + } + +} diff --git a/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkSubmissionServlet.java b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkSubmissionServlet.java new file mode 100644 index 0000000..3a97580 --- /dev/null +++ b/knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkSubmissionServlet.java @@ -0,0 +1,61 @@ +package org.argeo.support.odk.servlet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** Receives a form submission. */ +public class OdkSubmissionServlet extends HttpServlet { + private static final long serialVersionUID = 7834401404691302385L; + private final static Log log = LogFactory.getLog(OdkSubmissionServlet.class); + + private final static String XML_SUBMISSION_FILE = "xml_submission_file"; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + for (Part part : req.getParts()) { + if (log.isDebugEnabled()) + log.debug("Part: " + part.getName() + ", " + part.getContentType()); + } + Part xmlSubmissionPart = req.getPart(XML_SUBMISSION_FILE); + if (xmlSubmissionPart == null) + throw new ServletException("No " + XML_SUBMISSION_FILE + " part"); + try (InputStream in = xmlSubmissionPart.getInputStream();) { + // pretty print + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + StreamResult result = new StreamResult(new StringWriter()); + StreamSource source = new StreamSource(in); + transformer.transform(source, result); + String xmlString = result.getWriter().toString(); + System.out.println(xmlString); + } catch (TransformerException e) { + e.printStackTrace(); + } + + resp.setContentType("text/xml; charset=utf-8"); + resp.setHeader("X-OpenRosa-Version", "1.0"); + resp.setDateHeader("Date", System.currentTimeMillis()); + resp.setIntHeader("X-OpenRosa-Accept-Content-Length", 1024 * 1024); + resp.setStatus(201); + resp.getWriter().write("" + + "Form Received!" + ""); + + } +} diff --git a/knowledge/pom.xml b/knowledge/pom.xml new file mode 100644 index 0000000..b709290 --- /dev/null +++ b/knowledge/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + org.argeo.suite + argeo-suite + 2.1.16-SNAPSHOT + .. + + knowledge + Argeo Knowledge components + pom + + org.argeo.support.odk + + diff --git a/pom.xml b/pom.xml index 6859da9..eeafc8d 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,10 @@ org.argeo.suite.ui.rap org.argeo.suite.theme.default + + knowledge + + dep dist lib