Work on ODK servlets based on JCR.
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 3 Nov 2020 07:45:48 +0000 (08:45 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 3 Nov 2020 07:45:48 +0000 (08:45 +0100)
15 files changed:
knowledge/org.argeo.support.odk/OSGI-INF/odkFormListServlet.xml
knowledge/org.argeo.support.odk/OSGI-INF/odkFormServlet.xml
knowledge/org.argeo.support.odk/OSGI-INF/odkSubmissionServlet.xml
knowledge/org.argeo.support.odk/bnd.bnd
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/OrxListType.java
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/odk.cnd
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormListServlet.java
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkFormServlet.java
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkServletContext.java
knowledge/org.argeo.support.odk/src/org/argeo/support/odk/servlet/OdkSubmissionServlet.java
org.argeo.entity.api/src/org/argeo/entity/EntityType.java
org.argeo.entity.api/src/org/argeo/entity/JcrName.java [new file with mode: 0644]
org.argeo.entity.api/src/org/argeo/entity/entity.cnd
org.argeo.suite.core/src/org/argeo/suite/RankedObject.java
org.argeo.suite.core/src/org/argeo/suite/core/SuiteMaintenanceService.java

index 852423b16a199c9103172e916433e691645f6a19..e290933fd9bfd91701dc9ef685a5c6262ae1c2fe 100644 (file)
@@ -7,4 +7,5 @@
    <property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/formList"/>
    <property name="osgi.http.whiteboard.context.select" type="String" value="(osgi.http.whiteboard.context.name=odkServletContext)"/>
    <reference bind="addForm" cardinality="0..n" interface="org.argeo.support.odk.OdkForm" name="OdkForm" policy="dynamic" unbind="removeForm"/>
+   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=odk)"/>
 </scr:component>
index 59d2de217fc8180378ccc2cbcb808df39547bc8c..7f7e4dbfdf64076913ab875470a96b0e86ef6024 100644 (file)
@@ -4,7 +4,8 @@
    <service>
       <provide interface="javax.servlet.Servlet"/>
    </service>
-   <property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/*.xml"/>
+   <property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/form/*"/>
    <property name="osgi.http.whiteboard.context.select" type="String" value="(osgi.http.whiteboard.context.name=odkServletContext)"/>
    <reference bind="addForm" cardinality="0..n" interface="org.argeo.support.odk.OdkForm" name="OdkForm" policy="dynamic" unbind="removeForm"/>
+   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=odk)"/>
 </scr:component>
index e9153b4b9f0b5788bca4dc693f0366e59c693583..1e1bcb1277143346cead26d6d7f73fbc64ae429d 100644 (file)
@@ -7,4 +7,5 @@
    <property name="osgi.http.whiteboard.servlet.pattern" type="String" value="/submission"/>
    <property name="osgi.http.whiteboard.context.select" type="String" value="(osgi.http.whiteboard.context.name=odkServletContext)"/>
    <property name="osgi.http.whiteboard.servlet.multipart.enabled" type="String" value="true"/>
+   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="static" target="(cn=odk)"/>
 </scr:component>
index 619b258f6b34bdbdc20185dd5043a7f2afa4dcd0..01549855f63d922168494419e640589cc00d0397 100644 (file)
@@ -13,5 +13,5 @@ OSGI-INF/odkSubmissionServlet.xml
 
 Import-Package:\
 org.osgi.service.http.context,\
-org.argeo.naming,\
+org.argeo.api,\
 *
index 0b7a963ae48923fe9fecc91f197018e998071f5f..c51484c0109eeb9013f31af9bbf28f0eb2bbaab6 100644 (file)
@@ -1,9 +1,9 @@
 package org.argeo.support.odk;
 
-import org.argeo.jcr.QualifiedName;
+import org.argeo.entity.JcrName;
 
 /** Types related to the http://openrosa.org/xforms/xformsList namespace. */
-public enum OrxListType implements QualifiedName {
+public enum OrxListType implements JcrName {
        xform;
 
        @Override
index c3f43c842daf91a6f211a6b8afe8e8e1f8d205cd..0f6fcc8f93cda9313b3b4b9c691d02527342211c 100644 (file)
@@ -4,7 +4,7 @@
 <orxManifest = "http://openrosa.org/xforms/xformsManifest">
 <odk = "http://www.opendatakit.org/xforms">
 
-[orxList:xform]
+[orxList:xform] > mix:created, mix:lastModified, jcrx:csum, entity:form
 + h:html (odk:html) = odk:html
 
 [odk:head]
index 8398c7fa54769a3110aa6eaac54a7dbb7554b8a2..cc892a8b3c195ef0424d0277e11d221d70361f5e 100644 (file)
@@ -2,10 +2,21 @@ package org.argeo.support.odk.servlet;
 
 import java.io.IOException;
 import java.io.Writer;
+import java.security.AccessControlContext;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
+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.security.auth.Subject;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -13,7 +24,15 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.argeo.api.NodeConstants;
+import org.argeo.api.NodeUtils;
+import org.argeo.cms.auth.ServletAuthUtils;
+import org.argeo.entity.EntityType;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.JcrxApi;
 import org.argeo.support.odk.OdkForm;
+import org.argeo.support.odk.OrxListType;
 
 /** Lists available forms. */
 public class OdkFormListServlet extends HttpServlet {
@@ -22,6 +41,11 @@ public class OdkFormListServlet extends HttpServlet {
 
        private Set<OdkForm> odkForms = Collections.synchronizedSet(new HashSet<>());
 
+       private DateTimeFormatter versionFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd-HHmm")
+                       .withZone(ZoneId.from(ZoneOffset.UTC));
+
+       private Repository repository;
+
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                resp.setContentType("text/xml; charset=utf-8");
@@ -32,27 +56,101 @@ public class OdkFormListServlet extends HttpServlet {
                int serverPort = req.getServerPort();
                String protocol = serverPort == 443 || req.isSecure() ? "https" : "http";
 
+               String pathInfo = req.getPathInfo();
+
+               Subject subject = Subject
+                               .getSubject((AccessControlContext) req.getAttribute(AccessControlContext.class.getName()));
+               log.debug("SERVLET CONTEXT: " + subject);
+
+               Session session = ServletAuthUtils.doAs(() -> Jcr.login(repository, NodeConstants.SYS_WORKSPACE), req);
+//             session = NodeUtils.openDataAdminSession(repository, NodeConstants.SYS_WORKSPACE);
                Writer writer = resp.getWriter();
                writer.append("<?xml version='1.0' encoding='UTF-8' ?>");
                writer.append("<xforms xmlns=\"http://openrosa.org/xforms/xformsList\">");
-               for (OdkForm form : odkForms) {
-                       StringBuilder sb = new StringBuilder();
-                       sb.append("<xform>");
-                       sb.append("<formID>" + form.getFormId() + "</formID>");
-                       sb.append("<name>" + form.getName() + "</name>");
-                       sb.append("<version>" + form.getVersion() + "</version>");
-                       sb.append("<hash>" + form.getHash(null) + "</hash>");
-                       sb.append("<descriptionText>" + form.getDescription() + "</descriptionText>");
-                       sb.append("<downloadUrl>" + protocol + "://" + serverName
-                                       + (serverPort == 80
-                                                       || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/" + form.getFileName()
-                                       + "</downloadUrl>");
-                       sb.append("</xform>");
-                       String str = sb.toString();
-                       if (log.isDebugEnabled())
-                               log.debug(str);
-                       writer.append(str);
-               }
+               boolean newApproach = true;
+               if (newApproach)
+                       try {
+
+//                     Query query;
+//                     if (pathInfo == null) {
+////                           query = session.getWorkspace().getQueryManager()
+////                                           .createQuery("SELECT * FROM [nt:unstructured]", Query.JCR_SQL2);
+//                             query = session.getWorkspace().getQueryManager()
+//                                             .createQuery("SELECT * FROM [" + OrxListType.xform.get() + "]", Query.JCR_SQL2);
+//                     } else {
+//                             query = session.getWorkspace().getQueryManager()
+//                                             .createQuery(
+//                                                             "SELECT node FROM [" + OrxListType.xform.get()
+//                                                                             + "] AS node WHERE ISDESCENDANTNODE (node, '" + pathInfo + "')",
+//                                                             Query.JCR_SQL2);
+//                     }
+//                     QueryResult queryResult = query.execute();
+//
+//                     NodeIterator nit = queryResult.getNodes();
+                               log.debug(session.getUserID());
+                               log.debug(session.getWorkspace().getName());
+                               NodeIterator nit = session.getRootNode().getNodes();
+                               while (nit.hasNext()) {
+                                       log.debug(nit.nextNode());
+                               }
+                               while (nit.hasNext()) {
+                                       StringBuilder sb = new StringBuilder();
+                                       Node node = nit.nextNode();
+                                       if (node.isNodeType(OrxListType.xform.get())) {
+                                               sb.append("<xform>");
+                                               sb.append("<formID>" + node.getNode("h:html").getIdentifier() + "</formID>");
+                                               sb.append("<name>" + node.getProperty(Property.JCR_TITLE).getString() + "</name>");
+                                               sb.append("<version>" + versionFormatter.format(JcrUtils.getModified(node)) + "</version>");
+                                               sb.append("<hash>md5:" + JcrxApi.getChecksum(node, JcrxApi.MD5) + "</hash>");
+                                               if (node.hasProperty(Property.JCR_DESCRIPTION))
+                                                       sb.append("<name>" + node.getProperty(Property.JCR_DESCRIPTION).getString() + "</name>");
+                                               sb.append("<downloadUrl>" + protocol + "://" + serverName
+                                                               + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/form/"
+                                                               + node.getPath() + "</downloadUrl>");
+                                               sb.append("</xform>");
+                                       } else if (node.isNodeType(EntityType.formSet.get())) {
+                                               sb.append("<xforms-group>");
+                                               sb.append("<groupId>" + node.getPath() + "</groupId>");
+                                               sb.append("<name>" + node.getProperty(Property.JCR_TITLE).getString() + "</name>");
+                                               sb.append("<listUrl>" + protocol + "://" + serverName
+                                                               + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/formList"
+                                                               + node.getPath() + "</listUrl>");
+                                               sb.append("</xforms-group>");
+                                       }
+                                       String str = sb.toString();
+                                       if (!str.equals("")) {
+                                               if (log.isDebugEnabled())
+                                                       log.debug(str);
+                                               writer.append(str);
+                                       }
+                               }
+                       } catch (RepositoryException e) {
+                               e.printStackTrace();
+                               // TODO error message
+                               // resp.sendError(500);
+                       } finally {
+                               Jcr.logout(session);
+                       }
+
+               boolean oldApproach = true;
+               if (oldApproach)
+                       for (OdkForm form : odkForms) {
+                               StringBuilder sb = new StringBuilder();
+                               sb.append("<xform>");
+                               sb.append("<formID>" + form.getFormId() + "</formID>");
+                               sb.append("<name>" + form.getName() + "</name>");
+                               sb.append("<version>" + form.getVersion() + "</version>");
+                               sb.append("<hash>" + form.getHash(null) + "</hash>");
+                               sb.append("<descriptionText>" + form.getDescription() + "</descriptionText>");
+                               sb.append("<downloadUrl>" + protocol + "://" + serverName
+                                               + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort) + "/api/odk/form/"
+                                               + form.getFileName() + "</downloadUrl>");
+                               sb.append("</xform>");
+                               String str = sb.toString();
+                               if (log.isDebugEnabled())
+                                       log.debug(str);
+                               writer.append(str);
+                       }
                writer.append("</xforms>");
        }
 
@@ -63,4 +161,9 @@ public class OdkFormListServlet extends HttpServlet {
        public void removeForm(OdkForm odkForm) {
                odkForms.remove(odkForm);
        }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
 }
index 3746da852aaefbb0826cc32673a40fec0b53266d..93f41479ded820342874e326a9ced9631af4db66 100644 (file)
@@ -7,35 +7,57 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
 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.cms.auth.ServletAuthUtils;
+import org.argeo.jcr.Jcr;
 import org.argeo.support.odk.OdkForm;
 
 /** Retrieves a single form. */
 public class OdkFormServlet extends HttpServlet {
        private static final long serialVersionUID = 7838305967987687370L;
 
+       private Repository repository;
        private Map<String, OdkForm> 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);
+               Session session = ServletAuthUtils.doAs(() -> Jcr.login(repository, null), req);
+
+               String pathInfo = req.getPathInfo();
+
+               try {
+                       if (session.nodeExists(pathInfo)) {
+                               session.exportDocumentView(pathInfo + "/h:html", resp.getOutputStream(), true, false);
+                       } else {
+
+                               String fileName = FilenameUtils.getName(pathInfo);
+                               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);
+                               }
+                       }
+               } catch (RepositoryException e) {
+                       e.printStackTrace();
+                       // TODO error message
+                       resp.sendError(500);
+               } finally {
+                       Jcr.logout(session);
                }
        }
 
@@ -47,4 +69,8 @@ public class OdkFormServlet extends HttpServlet {
                odkForms.remove(odkForm.getFileName());
        }
 
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
 }
index 2c37edccbdf99985273f58c3396de9a681eeed05..1711f2dfa6e7599a62fe5d8bcbe52d37a635d1e9 100644 (file)
@@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.argeo.cms.servlet.PrivateWwwAuthServletContext;
 
+/** OSK specific authentication (with additional headers).*/
 public class OdkServletContext extends PrivateWwwAuthServletContext {
 
        @Override
index 3a975804b69ccf8c6781329fb3adff5ede6071a9..e5f5a3ffa6310cda67599c417f98d6b5f94246fa 100644 (file)
@@ -4,6 +4,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringWriter;
 
+import javax.jcr.Repository;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -26,6 +27,8 @@ public class OdkSubmissionServlet extends HttpServlet {
 
        private final static String XML_SUBMISSION_FILE = "xml_submission_file";
 
+       private Repository repository;
+
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                for (Part part : req.getParts()) {
@@ -58,4 +61,9 @@ public class OdkSubmissionServlet extends HttpServlet {
                                + "<message>Form Received!</message>" + "</OpenRosaResponse>");
 
        }
+       
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
 }
index 00a7803ecaf92c54f179596207403bb23079df78..6548d7599578793d2f8494621406ad3e9d45aaca 100644 (file)
@@ -1,9 +1,7 @@
 package org.argeo.entity;
 
-import org.argeo.jcr.QualifiedName;
-
 /** Types related to entities. */
-public enum EntityType implements QualifiedName {
+public enum EntityType implements JcrName {
        // entity
        entity, definition,
        // typology
diff --git a/org.argeo.entity.api/src/org/argeo/entity/JcrName.java b/org.argeo.entity.api/src/org/argeo/entity/JcrName.java
new file mode 100644 (file)
index 0000000..43057aa
--- /dev/null
@@ -0,0 +1,26 @@
+package org.argeo.entity;
+
+/** Can be applied to {@link Enum}s in order to generate prefixed names. */
+public interface JcrName {
+       String name();
+
+       default String getPrefix() {
+               return null;
+       }
+
+       default String getNamespace() {
+               return null;
+       }
+
+       default String get() {
+               String prefix = getPrefix();
+               return prefix != null ? prefix + ":" + name() : name();
+       }
+
+       default String withNamespace() {
+               String namespace = getNamespace();
+               if (namespace == null)
+                       throw new UnsupportedOperationException("No namespace is specified for " + getClass());
+               return "{" + namespace + "}" + name();
+       }
+}
index 618487b572187d0fdc0b025df073dd7dda260cf0..78fb8ccb1c0f6fc7d1b530ce82be1ec839c39347 100644 (file)
@@ -15,12 +15,12 @@ mixin
 //
 // ENTITY DEFINITION
 //
-[entity:definition] > entity:composite, mix:created, mix:referenceable
+[entity:definition] > entity:composite, mix:created, mix:lastModified, mix:referenceable
 //- entity:type (String) multiple
 
-[entity:part]
+//[entity:part]
 
-[entity:reference]
+//[entity:reference]
 
 [entity:composite]
 orderable
index f0165af75f3905a88a2580e6ddd37caae0df72ea..bfba46e449879f6cfa717daafc70d4d815c064a2 100644 (file)
@@ -76,16 +76,16 @@ public class RankedObject<T> {
                RankedObject<T> rankedObject = new RankedObject<>(object, properties);
                if (!map.containsKey(key)) {
                        map.put(key, rankedObject);
-                       if (log.isDebugEnabled())
-                               log.debug(
+                       if (log.isTraceEnabled())
+                               log.trace(
                                                "Added " + key + " as " + object.getClass().getName() + " with rank " + rankedObject.getRank());
                        return rankedObject;
                } else {
                        RankedObject<T> current = map.get(key);
                        if (current.getRank() <= rankedObject.getRank()) {
                                map.put(key, rankedObject);
-                               if (log.isDebugEnabled())
-                                       log.debug("Replaced " + key + " by " + object.getClass().getName() + " with rank "
+                               if (log.isTraceEnabled())
+                                       log.trace("Replaced " + key + " by " + object.getClass().getName() + " with rank "
                                                        + rankedObject.getRank());
                                return rankedObject;
                        } else {
index 1117c5e317843dfd06cf3c0699f45762ac815c3d..44d218c4764d89ec1c935993e5c2abc4059c2152 100644 (file)
@@ -18,7 +18,7 @@ public class SuiteMaintenanceService extends AbstractMaintenanceService {
                boolean modified = false;
                Node rootNode = adminSession.getRootNode();
                if (!rootNode.hasNode(EntityNames.TERM_BASE)) {
-                       rootNode.addNode(EntityNames.TERM_BASE, EntityType.typologies.qualified());
+                       rootNode.addNode(EntityNames.TERM_BASE, EntityType.typologies.get());
                        modified = true;
                }
                if (modified)